How Moti Animations work

evening kid
5 min readMay 12, 2021

Animations in React Native have always been difficult to make.

In the last few years, however, it seems that a lot of people have been working towards simpler animation tools.

You might have heard about Reanimated, which made animation easy and faster but also more recently about Moti. In today’s chapter, let’s learn about animations in Moti.

This was first published as a video for the How To Moti series. If you learn better through audio/image, I highly recommend it.

🤔 What is Moti?

Long story short, Moti doesn’t give you a better way to make animations but a different way of writing them.

If you need a fade-in effect in Reanimated, which is what Moti is based on, you need a shared value, a timed animation, and an animated style:

function FadeEffect() {
const opacity = useSharedValue(0);

useEffect(() => {
opacity.value = withTiming(1);
}, []);

const animatedStyle = useAnimatedStyle(() => {
return {
opacity: opacity.value,
};
});


return <Animated.View style={animatedStyle} />;
}

Basically, you are describing all the animation logic outside of the rendering part of your component.

On the other hand, with Moti, you can get rid of all the hooks and declare animations right from the view properties:

// Not your regular react-native View
import { View } from 'moti';
function FadeEffect() {
return (
<View
from={{ opacity: 0 }}
animate={{ opacity: 1 }}

/>
);
}

On top of this, you can of course add a lot more such as other properties, delays, types of animations:

<View
from={{ opacity: 0, translateY: -15 }}
animate={{ opacity: 1, translateY: 0 }}
delay={1000}
transition={{
type: 'spring',
}}

/>

You can even repeat one…

<View
...
transition={{
// Repeat animation 4 times
repeat: 4,
// Repeat animation indefinitely
loop: true,
}}
/>

…or create a series of them for a given element:

<View
animate={{ opacity: [0, 1, 0] }}
/>

Opacity here will be 0, then 1, then 0 again.

Now obviously, there’s no magic here. Moti is essentially recreating all the Reanimated logic for you in the background.

So the great part about it is definitely how simpler it looks from a web developer perspective, where CSS animations and keyframes are common practice.

But you might be thinking this works well for simple animations. What if you need more control and configuration?

🎒 Animation State

The problem you could think about is “how to trigger animations with Moti?”.

Since you set the animation through props, it will run as soon as it is mounted, as it appears on the screen.

But in most cases, you’ll probably want to animate a View differently based on its current state. If a text input value is wrong, the border could turn red, or green if it’s correct.

Meaning a text input could have three different styles that need to be activated based on certain criteria. With Moti, you can do this through an animation state and variants.

The idea is that instead of animating a component upon mounting, you want to trigger an animation to transition it from one style to another.

And to do this, we’ll use a hook this time that defines a list of possible styles, called variants:

import { useAnimationState } from 'moti';const animationState = useAnimationState({
success: {
borderColor: 'green',
},
fail: {
borderColor: 'red',
},
});

You then can remove all the properties that were on your element and pass this state to your Moti component:

import { TextInput } from 'react-native';
import { useAnimationState, motify } from 'moti';
// Works just like Animated.createAnimatedComponent
const MotiTextInput = motify(TextInput)();
const animationState = useAnimationState({ ... });return <MotiTextInput state={animationState} />;

You could think of this as CSS classes in web, where you’d conditionally add them to an element with a transition property:

input {
border-color: gray;
transition: border-color 250ms;
}
input.success {
border-color: green;
}
input.fail {
border-color: red;
}

The only thing to know here is how to transition the component from one style to another. To do so, you can trigger it through the animation state transitionTo method that takes any variant name.

So if we want to apply the “success” style, we could write the following:

const animationState = useAnimationState({ ... });useEffect(() => {
// Or .transitionTo('fail')
animationState.transitionTo('success');
}, []);

Now, maybe in a more real-life scenario, you could do this based on what’s written inside the text input and therefore, transition either to the success or fail variant:

function TextField() {
const [text, setText] = useState('');

const animationState = useAnimationState({
success: {
borderColor: 'green',
},
fail: {
borderColor: 'red',
},
});

useEffect(() => {
if (text === 'password') {
animationState.transitionTo('success');
} else {
animationState.transitionTo('fail');
}

}, [text]);

return (
<MotiTextInput
state={animationState}
value={text}
onChangeText={setText}

/>
);
}

Here as you can see, we’re just checking if the current text is “password”, and transitioning to the adequate variant.

This is more performant as we define the list of possible styles this element could transition to. But what if this should be more dynamic?

Because yes, actually, these variants can’t change over time meaning you couldn’t define a style property based on a changing variable.

For example, if this input can be disabled, it will keep the first value of this prop and not recreate a variant when it changes:

const animationState = useAnimationState({
success: {
borderColor: props.disabled
? 'green'
: 'lightgreen',

},
fail: {
borderColor: 'red',
},
});

In most cases, this is okay, but if you ever need this, let’s see how it can be more dynamic.

🏎 Dynamic Animation

So now that you know about variant-based, static animation states, we can look at dynamic animations in Moti.

It basically works like a state for your styles: you pass it a list of the default properties…

const animation = useDynamicAnimation(() => {
return {
opacity: 1,
};
});
// Similar to
// this.state = { opacity: 1 };
return <View state={animation} />;

…and then you can trigger changes just as if you were using setState:

useEffect(() => {
animation.animateTo({ opacity: 0 });

// Similar to
// this.setState({ opacity: 1 });
}, []);

This is great, and it gives you much more flexibility without compromising performance.

There’s not much more to know about animations in Moti, but there are however some handy components you might want to learn about in the documentation.

📕 Recap

If there are three things to remember today, here they are:

  • Moti is a wrapper around Reanimated and gives you a new way to write your animations.
  • Every component can be animated through its properties (from, to, delay, transition…) or an animation state using variants (useAnimationState, transitionTo).
  • Styles can be changed on the go without compromising performance using dynamic animations (useDynamicAnimation).

Alright, that’s it for today. In the next chapter, we’ll look at gestures in Moti.

New articles will be released on a weekly-basis. Remember to follow me if you don’t want to miss any.

--

--

evening kid

I often find myself reading too many articles on the internet