Moti Interactions#

Snippet#
Video#
First, import:
Next, animate your interactions:
About#
The MotiPressable component lets you animate based on pressed and hovered interactions, without triggering any re-renders.
The usage is very similar to Pressable from react-native.
The two differences are 1) you use the animate prop rather than style, and 2) the function you pass must be a worklet, resulting in faster performance and a better developer experience.
Like always, MotiPressable relies on the native thread when tracking interactions.
Examples#
Check out these tweets from Fernando Rojo for more context:
- From Moti's API to MotiPressable
- Remaking BeatGig's web dropdown menu with moti interactions
- Animating children of a Pressable component without re-renders
Installation#
Versions before 0.17.0 required you to install @motify/interactions. This is no longer the case. See the PR.
Interactions now comes bundled with moti. You can import it like so:
Peer dependencies#
As of moti@0.17.0, moti/interactions uses react-native-gesture-handler v2. Upgrading may not be required, but it is recommended. This is the version included in Expo SDK 44.
You'll need to wrap your app with GestureHandlerRootView from react-native-gesture-handler. Please see their docs for installation instructions.
Animating children#
A common use-case of a component like Pressable is styling children based on the animation state.
Before moti, you might do this with the React Native Pressable component.
Moti takes a different approach to improve performance and composition.
Rather than using React state to track the interaction, Moti uses Reanimated shared values with React Native Gesture Handler.
As a result, Moti's interactions trigger zero re-renders, and all animations are handled on the native thread.
The Moti way#
Let's see what the above example might look like with Moti.
First, change Pressable to MotiPressable. Next, remove the function child, and instead pass a component directly. Let's call it Child.
Then, in the Child component, we can access the parent's interaction state with useMotiPressable
Access a specific parent#
useMotiPressable also optionally takes a unique id as its first argument. This lets you access a specific parent pressable's interaction.
For example, say you have a list component at the root, and you want to access its state:
Notice that the MotiPressable component now has an id="list" prop. This tells all of its children that it can be uniquely referred to as list.
By default, the useMotiPressable would access the interaction state of its closest MotiPressable parent. That default behavior doesn't work for this case, since we want to animate based on the top-level list component.
In the Child component, pass list as the first argument to useMotiPressable.
That's it. Now, useMotiPressable will return animate based on the interaction state of the outer-most MotiPressable.
Access multiple parents#
In the previous section, we saw how to access a unique parent's interaction state. But what if we want to combine the interaction states of multiple components for more complex animations?
That's easy too. Our previous example looked like this:
Let's add a unique id prop to each MotiPressable item that's rendered in the list, called item-${id}.
Let's also pass the id as a prop to the Child.
Now, the Child component can call useMotiPressables instead of useMotiPressable.
Say we want to make all items fade away when you hover over the list, except for the actual item you're hovering.
Animated props#
Let's say you want to update a child component's props based on a parent's interaction state.
For example, you have a dropdown menu whose pointerEvents should be none when its container isn't hovered.
You can also pass a TypeScript generic to useMotiPressableAnimatedProps:
useMotiPressableAnimatedProps relies on useAnimatedProps under the hood.
animatedPropscannot be used withanimateon the same prop on Web. If you need to do both, please split your usage into two components; one that receives theanimateprop, and another that receivesanimateProps. This is a reanimated limitation.
Interpolate interaction state#
A rare but available use-case is useInterpolateMotiPressable.
As the name implies, this lets you access the shared value state of a parent pressable.
Example:
If you're passing a unique id prop to your pressable, you can also isolate this hook to that pressable.
Say the parent pressable has id="list", and you want to isolate this hook to the list pressable:
Then, in the Item component:
It returns an Animated.DerivedValue. You can also type it with a generic:
Just like any derived value, you can read the value it returns with .value:
Performance#
By default, this component should have better performance than the native Pressable. It triggers zero re-renders, and all animations are run on the native thread.
If your component re-renders often, consider wrapping your hook with useCallback to improve performance:
For all the hooks provided, you can also pass a dependency array to improve performance. It works just like the dependency array for useMemo, and it's always the last argument for any of the pressable hooks.
Web Support#
MotiPressable provides first-class support for Web, including hovered and pressed interactions.
Please note that Reanimated 2 uses JS animations on Web. That said, MotiPressable still doesn't trigger re-renders on web.