React Pose for easy and amazing animations
A quick quide to using Pose animation library with React and producing beautiful UI animations
Published: 27 November 2019
npm install react-pose --save
Steps are as follows:
To make things work we must "recreate" any basic HTML element that we are trying to animate like div, span, etc with React Pose as follows:
import posed from "react-pose"
const NewPosedDiv = posed.div() //creates a div component which can be animated
This new div can now be given some poses aka states which React Pose will show under different situations.
import posed from "react-pose"
const NewPosedDiv = posed.div({
shown: { opacity: 1 }, // we can stipulate in JSX when to apply this pose
hidden: { opacity: 0 }, // or this pose
})
NewPosedDiv
can now be used as a div inside our JSX like so:
//always declare posed components outside render/return block
const NewPosedDiv = posed.div()
const MyComponent = () => {
return (
<NewPosedDiv>
<h1>I am a heading</h1>
<p>I am some random paragraph</p>
</NewPosedDiv>
)
}
export default MyComponent
NOTE: Make sure that any new elements created with React pose should be declared outside of the render function (in class based components) and outside the return statement (in functional components)
Pose works by detecting changes in the state. If a component is declared outside the render or return blocks, it won't be re-rendered as a brand new component with each render cycle. This will allow pose to detect any changes and show necessary animations/poses.
Declare the conditions under which different poses should be applied.
Lets say MyComponent receives a boolean prop startAnimation, which dictates when to show the animation.
To achieve this, we can pass a pose prop to the NewPosedDiv
const MyComponent = props => {
return (
<NewPosedDiv pose={props.startAnimation ? "shown" : "hidden"}>
<h1>I am a heading</h1>
<p>I am some random paragraph</p>
</NewPosedDiv>
)
}
We have now stipulated that if startAnimation is true, we want the shown pose to be applied to our NewPosedDiv, otherwise use hidden pose.
Also, NewPosedDiv doesn't need to be a root level element, we could have just as easily nested it inside a normal div like so:
const MyComponent = props => {
return (
<div>
<p>Paragraph doing not much</p>
<NewPosedDiv pose={props.startAnimation ? "shown" : "hidden"}>
<h1>I am a heading</h1>
<p>I am some random paragraph</p>
</NewPosedDiv>
</div>
)
}
As a side note, we haven't declared what the animation will look like. We only provided two states and React Pose took care of the rest behind the scenes.
By default, it uses a tween type animation which looks amazing.
We can add as many animatable css properties as we like to a single pose object:
const NewPosedDiv = posed.div({
shown: {
opacity: 1,
background: "#af55b7", //make sure to use HEX or RGB values only
height: "100vh",
},
hidden: {
opacity: 0,
background: "#b4b1e8", //make sure to use HEX or RGB values only
height: 0,
},
})
We can also add a transition key to any pose to indicate the duration, delay, type and many other properties of the overall pose as well as of individual properties:
const NewPosedDiv = posed.div({
shown: {
opacity: 1,
//applied to overall pose
transition: {
duration: 2000,
delay: 300,
type: "spring",
},
},
hidden: {
opacity: 0,
//OR to each prop
transition: {
opacity: { duration: 1000, delay: 100 }, //each prop inidividually
default: { duration: 500 }, // or a default for applied to every prop
},
},
})
transition can also be a function and runs once for each css property of the pose. It also receives an object as an argument which contains some default built-in props as well as any props we pass to the posed component:
Some of the built in props:
key - the name of the current property that is being looped over
to - value to set for the “key” that is currently being looped over
from - value to override of the current key (This is the old value which is to be replaced)
In either case, it must still return an object containing properties as shown in above code snippets.
const NewPosedDiv = posed.div({
shown: {
width:(props) => `${props.width + 10}%`, //each key can be a function to access props
transition: (props) => {
return {
duration: 500,
to: props.key === "width"
? props.width + "%" //accessing the passed width prop
: props.to
}
},
},
hidden:{ width:0% }
})
const MyComponent = props => {
return (
<NewPosedDiv
pose={props.startAnimation ? "shown" : "hidden"}
width="50" //passing our own width prop
>
<h1>I am a heading</h1>
<p>I am some random paragraph</p>
</NewPosedDiv>
)
}
Components created with React Pose, also receive some built in states like "enter" or "exit"
These can be useful when we are trying to animate a whole page or a whole route inside react router.
const NewPosedDiv = posed.div({
enter: { y: 0, opacity: 1, delay: 300 },
exit: {
y: 50,
opacity: 0,
transition: { duration: 200 },
},
})
We can also animate children of any posed components in 2 ways:
(1) Make all children posed components as well
//Main posed component
const PosedSideMenu = posed.div({
visible: { width: "30%", delayChildren: 200, staggerChildren: 50 },
hidden: { width: "0%" },
})
// list of posed components as children
const PosedSideMenuItems = posed.li({
show: {
opacity: 1,
transition: ({ delay }) => ({ delay, duration: 500 }),
},
hide: { opacity: 0 },
})
//Component to render at the end
const Menu = props => {
return (
<PosedSideMenu pose={props.show ? "visible" : "hidden"}>
<ul>
{["First", "Second", "Third"].map((item, i) => (
<PosedSideMenuItems key={item} pose={props.show ? "show" : "hide"}>
{item}
</PosedSideMenuItems>
))}
</ul>
</PosedSideMenu>
)
}
NOTE: Rather than animating all the children in at once, it’s possible to stagger them in individually. The staggerChildren prop can be used to determine the delay between each one, starting from after the delayChildren duration.
staggerDirection can be used to determine which order we stagger over the children in. It can either be 1 (first to last, default), or -1 (last to first).
Setting either beforeChildren or afterChildren props to true will make the parent animation play before or after any children animations.
(2) PoseGroup
Use PoseGroup to animate all children components regardless of whether any of them are posed components or not. PoseGroup is a component which wraps all the other components and always needs to be mounted while the children get mounted/unmounted according to state changes. This is why it forms the root component in cases for full page animations.
So, the above menu component can be re-written as:
import posed, { PoseGroup } from "react-pose"
//Component to render at the end
const Menu = props => {
return (
<PoseGroup>
<PosedSideMenu pose={props.show ? "visible" : "hidden"}>
{["First", "Second", "Third"].map((item, i) => (
<li key={item}>{item}</li>
))}
</PosedSideMenu>
{/* could add more children if needed */}
{/*
<PosedSideMenu pose={props.show ? "visible" : "hidden"}>
{["Fourth", "Fifth", "Sixth"].map((item, i) => (
<li key={item}>{item}</li>
))}
*/}
</PosedSideMenu>
</PoseGroup>
)
}