animate-expo
React Native and Expo animations: build UI-thread motion, gestures, sheets, screen transitions, press feedback, and haptics with Reanimated 4, Gesture…
Skill by joaodavidsilva
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/joaodavidsilva-animate-expo
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: spreadsheets, design, frontend
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/animate-expo
What's inside
- Skill name
animate-expo- Description
- React Native and Expo animations: build UI-thread motion, gestures, sheets, screen transitions, press feedback, and haptics with Reanimated 4, Gesture Handler, Expo Router, and expo-haptics. Use when animating an Expo or React Native app, adding gestures or haptics, or fixing mobile motion jank.
- License
- MIT
- Instructions
- 201 lines in SKILL.md · view on GitHub
Files (3)
- SKILL.md
- openai.yaml · agents/
- RECIPES.md · reference/
SKILL.md (first lines)
# Building Animations in Expo
A construction skill for React Native. It turns a request for motion into an implementation that survives a strict review on a real device, not in the simulator and not on a flagship phone in dev mode.
Mobile changes three things about animation, and everything in this skill follows from them:
1. **There is no hover.** Every affordance the web puts in hover has to live in press, position, or nothing.
2. **There are two runtimes.** Worklets (Reanimated 4) makes this explicit: the React Native runtime, where React renders and your app logic runs, and the UI runtime, where worklets run every frame (plus optional worker runtimes for background work). An animation that touches the RN runtime stutters the moment the app does anything else. The whole craft is keeping motion on the UI runtime.
3. **The user's finger is on the element.** Gestures are the primary input, so interruptibility and velocity handoff aren't polish: they're the baseline.
## Hard Rules
1. **Run the sequence in order.** Steps 1 and 2 gate everything.
2. **Reanimated and Gesture Handler, not core `Animated` or `PanResponder`.** Core `Animated` and `PanResponder` cross the bridge on gestures, and `useNativeDriver` refuses anything beyond transform and opacity. Reanimated worklets and `Gesture.Pan()` run on the UI thread and stay smooth while JS is busy.
3. **Exact curves and spring configs.** Take easing curves and spring parameters directly from step 5 and [reference/RECIPES.md](reference/RECIPES.md).
4. **Reduced motion ships with the animation**, not as a follow-up.
5. **Feel is judged on a release build on the slowest supported device**, never in Expo Go or the simulator.
## The Build Sequence
### 1. Should this animate at all?
Call the Skill tool with `"interface-polish"` and read `reference/ANIMATIONS.md` for the universal frequency gate.
Mobile addition: **Tab switches never slide (`animation: 'none'`).** Tabs are peers, not a hierarchy; sliding implies depth that isn't there, and the user pays for it dozens of times a session.
If the request fails this gate, say so and stop here.
Done when you have classified the interaction's frequency tier and either rejected the animation or confirmed it passes the gate.
### 2. What is the purpose?
Call the Skill tool with `"interface-polish"` and read `reference/ANIMATIONS.md` for the valid animation purposes. Name this animation's purpose in one word before continuing. If you cannot name it, do not build it.
Done when the animation's single purpose is named in one word.
### 3. Pick the tool: cheapest that works
Walk down; stop at the first that fits.
| Need | Tool |
| --- | --- |
| A state-driven change with no gesture (press, toggle, color, a value flipping) | **Reanimated CSS transition** (`transitionProperty` in the style) |
| Loop, multi-stage, or plays on mount with no state change | **Reanimated CSS animation** (`animationName` keyframes) |
| An element mounting or unmounting, or a list reflowing | **Layout animations** (`entering` / `exiting` / `itemLayoutAnimation`; on virtualized lists like `FlatList` or `FlashList`, animate the list container on mount or use `itemLayoutAnimation` for reflow rather than putting `entering` on recycled rows) |
| Anything a finger touches, or anything derived from scroll | **`useSharedValue` + `Gesture.Pan()` + `useAnimatedStyle`** |
| Screen to screen | **Native stack options in Expo Router** (`animation`, `presentation`) |
| A bottom sheet that is its own screen | **`presentation: 'formSheet'`**: it's a real UISheetPresentationController, free and correct |
| Tab bar | **`NativeTabs`** (from `expo-router/unstable-native-tabs`): the platform's real tab bar, its behaviors and transitions included |
| Context menu, press-and-hold preview | **`Link.Menu` / `Link.Preview`** (Expo Router, iOS-only): native menus and peek, never rebuilt in JS |
| Header that collapses into a large title | **`headerLargeTitleEnabled`** on the native stack (iOS-only; `headerLargeTitle` is deprecated), not a scroll worklet |
| Pull to refresh | **`RefreshControl`**: hand-roll only when it's a signature interaction (see the threshold recipe) |
| UI that tracks the keyboard | **`react-native-keyboard-controller`**: the keyboard's real position, frame by frame, on the UI thread |
| Vector illustration, celebration, empty state | **Lottie**: for illustration only, never for UI state |
| A huge animated scene, freeform drawing | **`@shopify/react-native-skia`**: a canvas, for when the view hierarchy itself is the bottleneck |
Reach for a shared value only when the value is continuous or interruptible. A press scale is a CSS transition; a drag is a shared value. Using a worklet for a two-state toggle is the mobile equivalent of installing a motion library for a fade.
For package installation (`npx expo install`), root `GestureHandlerRootView` and `KeyboardProvider` setup, and shared imports, see [reference/RECIPES.md](reference/RECIPES.md).
Done when the lowest-tier tool that satisfies the interaction is selected.
### 4. Pick the properties
- **`transform` and `opacity` are free.** Everything else is a layout pass. `width`, `height`, `margin`, `padding`, `flex`, `top`, `left`, `gap` re-run Yoga on every frame for that node *and its siblings*.
- **The one exception: an absolutely positioned element with no children**, such as a tab pill or a progress bar fill. It's out of flow, so nothing else re-lays-out, and animating `width` keeps the corner radius that `scaleX` would smear.
- **Start scale entrances from `scale(0.9-0.96)` + `opacity: 0`** rather than `scale(0)`. Nothing in the real world appears from nothing.
- **`transform` is an array and order matters**: `[{ translateY }, { scale }]` scales after moving; reversed, the translate gets scaled too. Keep translate first unless you want the multiplication.
- **Crossfade a static pre-shadowed layer** instead of animating Android `elevation`, which re-renders the shadow every frame.
- **Crossfade the opacity of a static `BlurView`** instead of animating `BlurView` intensity, which re-renders the blur each frame on Android.
- **Percentages work in `translate`** and are relative to the element's own size: `translateY('100%')` moves a sheet by its own height whatever its content.
Done when every animated property is `transform` or `opacity` (or `width` on a childless absolutely positioned element).
### 5. Timing or spring
**If a finger was involved, use a spring.** Springs carry velocity through an interruption; timing curves restart. Everything else uses timing.
Reanimated's spring takes Apple's two designer parameters directly. Use this form, not mass/stiffness/damping:
| Interaction | Config |
| --- | --- |
| Default settle, no overshoot | `{ duration: 400, dampingRatio: 1 }` |
| Reposition / snap back after a drag | `{ duration: 400, dampingRatio: 0.8, velocity }` |
| Sheet, drawer | `{ duration: 300, dampingRatio: 0.8, velocity }` |
| Must not pass a hard edge | add `overshootClamping: true` |
**Bounce only when the gesture carried momentum.** Overshoot on a menu that faded in feels wrong; overshoot on a card you flicked feels right. Commit or dismiss gestures on **projected velocity or distance** so a quick flick succeeds without dragging past a distance threshold, and apply **rubber-band resistance** rather than a hard stop when dragging past a boundary (see `project` and `rubberband` in [reference/RECIPES.md](reference/RECIPES.md)).
**Easing**, for everything without a finger on it:
| Situation | Easing |
| --- | --- |
| Entering or exiting | `ease-out` |
| Moving / morphing on screen | `ease-in-out` |
| Constant motion (progress, marquee) | `linear` |
| Default | `ease-out` |
**Use `ease-out` as the default UI curve**, never `ease-in` (which starts slow and delays the moment the user is watching). Reanimated's built-ins are as weak as CSS's, so use the `EASE_OUT` (`Easing.bezier(0.23, 1, 0.32, 1)`), `EASE_IN_OUT` (`Easing.bezier(0.77, 0, 0.175, 1)`), and `EASE_SHEET` (`Easing.bezier(0.32, 0.72, 0, 1)`) constants from [reference/RECIPES.md](reference/RECIPES.md).
**Duration:**
| Element | Duration |
| --- | --- |
| Press feedback | 150ms |
| Toggle, chip, small state change | 150-200ms |
| Sheet, modal, drawer | spring, ~300ms perceived |
| Screen transition | the platform default (keep unmodified) |
Mobile UI animations stay under 300ms, same as web. The platform's own transitions are longer (iOS push is 350ms); match the platform for navigation, beat it everywhere else.
Done when every gesture-driven motion uses a velocity-preserving spring and every state-driven motion has an explicit curve and sub-300ms duration.
### 6. Keep it off the JS thread
This is the mobile-specific craft, and it's where most React Native motion dies.
- **Drive gesture and scroll styles from a shared value + `useAnimatedStyle`** rather than calling `setState` per frame. One React render per frame is the single biggest cause of jank in RN apps.
- **Call `scheduleOnRN(fn, ...args)` from `react-native-worklets` only in `onEnd` or at a `useAnimatedReaction` threshold** (replacing the deprecated `runOnJS(fn)(...args)`), never per frame inside `onUpdate` or a scroll handler.From joaodavidsilva/skills, MIT.
More from joaodavidsilva/skills
- 0.565score
animation-vocabulary
joaodavidsilva
Animation vocabulary: reverse-lookup a motion effect's exact term from a visual or feel description when the user asks 'what's it called when...'.
SkillAFreshness: AClaude Code +9 - 0.558score
break-ui
joaodavidsilva
Stress-test a UI component with realistic worst-case data behind a dev-only toggle and report what breaks.
SkillAFreshness: AClaude Code +9 - 0.558score
improve-animations
joaodavidsilva
Audit a codebase's animations or hunt for motion opportunities, then write self-contained implementation plans.
SkillAFreshness: AClaude Code +9 - 0.544score
code-review
joaodavidsilva
Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes: Standards (does the code follow this repo's documented coding…
SkillAFreshness: AClaude Code +9 - 0.536score
improve-codebase-architecture
joaodavidsilva
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
SkillAFreshness: AClaude Code +9 - 0.536score
setup-joao-david-silva-skills
joaodavidsilva
Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout.
SkillAFreshness: AClaude Code +9 - 0.533score
ask-joao
joaodavidsilva
Ask which skill or flow fits your situation.
SkillAFreshness: AClaude Code +9 - 0.532score
pr
joaodavidsilva
Use when writing a PR body.
SkillAFreshness: AClaude Code +9 - 0.530score
retro
joaodavidsilva
Conduct a retrospective on a coding session.
SkillAFreshness: AClaude Code +9 - 0.521score
interface-polish
joaodavidsilva
UI and animation polish: apply or review the details of animation (easing, duration, springs, origin-aware popovers, gestures), typography, surfaces (radius…
SkillBFreshness: BClaude Code +9 - 0.499score
prototype
joaodavidsilva
Build a throwaway prototype to answer a design question.
SkillBFreshness: BClaude Code +9 - 0.466score
commit
joaodavidsilva
Use when writing a commit message.
SkillCFreshness: CClaude Code +9
Signals
- Stars
- 0
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Sep 24, 2026 (1 month ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/joaodavidsilva/skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from joaodavidsilva
- 0.565score
animation-vocabulary
joaodavidsilva
Animation vocabulary: reverse-lookup a motion effect's exact term from a visual or feel description when the user asks 'what's it called when...'.
SkillAFreshness: AClaude Code +9 - 0.558score
break-ui
joaodavidsilva
Stress-test a UI component with realistic worst-case data behind a dev-only toggle and report what breaks.
SkillAFreshness: AClaude Code +9 - 0.558score
improve-animations
joaodavidsilva
Audit a codebase's animations or hunt for motion opportunities, then write self-contained implementation plans.
SkillAFreshness: AClaude Code +9 - 0.544score
code-review
joaodavidsilva
Review the changes since a fixed point (commit, branch, tag, or merge-base) along two axes: Standards (does the code follow this repo's documented coding…
SkillAFreshness: AClaude Code +9 - 0.536score
improve-codebase-architecture
joaodavidsilva
Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.
SkillAFreshness: AClaude Code +9 - 0.536score
setup-joao-david-silva-skills
joaodavidsilva
Configure this repo for the engineering skills: set up its issue tracker, triage label vocabulary, and domain doc layout.
SkillAFreshness: AClaude Code +9
Cite this page
animate-expo skill. Agent Mart. https://agentmart.dev/skills/joaodavidsilva-animate-expo (last updated 2026-10-24)