Go back

How to use react-spring with Chakra UI

To approach animation with a react library in a ChakraUI project, Framer motion is the recommended tool. Some of the micro-interactions in Chakra components rely on it, so it is just best to use it if you want to customize your interactions.

But all my attempts at using it always ended up wonky. I'd recommend going through the docs if you want to give it a try sometime.

Extend react-spring's HOC

If you're already familiar with react-spring and you don't want to go through another learning curve, all you'd need to extend react-spring in your project is to wrap any ChakraUI component in the animated HOC — Higher Order Component — exposed from react-spring.

Say you want to make a Box component "animate-able", you'll approach it like so:

homepage.tsx
import { useSpring, animated } from '@react-spring/web'
import { Box, Container } from '@chakra-ui/react'
 
const AnimatedBox = animated(Box)
 
export const HomePage = () => {
  const springs = useSpring({
    from: {
      y: 0,
    },
    to: {
      y: 30,
    },
  })
 
  return (
    <Container>
      <AnimatedBox
        boxSize="200px"
        background="blue"
        borderRadius="8px"
        style={{ ...springs }}
      >
        <Text>Animated Chakra Box</Text>
      </AnimatedBox>
    </Container>
  )
}

And that's all there is about it. All the chakra props for the Box component's styling can be used without any issues.

But, what if you want to have multiple components animated? It'll be tiring having to define those components one after the other. Instead, you can have a source of truth for all the components you need.

Create an animated components entrypoint

To approach this pattern, I create a folder called "externals" in my projects, just to make it accessible and distinct from the others, since it relies on a library external from Chakra. Yours could be different, you can decide to place these components in a "utils" folder. Whatever works for you.

Now, you'd just repeat the same process from the first snippet with the components you want animated

externals/index.ts
import { animated } from '@react-spring/web'
import { ListItem, UnorderedList, Text, Box } from '@chakra-ui/react'
 
export const AnimatedBox = animated(Box)
export const AnimatedText = animated(Text)
export const AimatedUnorderedList = animated(UnorderedList)
export const AnimatedListItem = animated(ListItem)

Since this works pretty well, we can make it better. The approach above would force us to use these components by importing them one after the other. There's nothing bad in doing so.

import { AnimatedBox, AnimatedText, AnimatedListItem } from '@externals'

But, to add a bit of "syntactic sugar", we can export an object containing all these components and use the dot notation to access the component(s) we need. This is quite common if you've tried out Framer motion before. It is the same with react-spring too — <animated.div />

externals/index.ts
import { animated } from '@react-spring/web'
import { ListItem, UnorderedList, Text, Box } from '@chakra-ui/react'
 
export const Animated = {
  Box: animated(Box),
  Text: animated(Text),
  ListItem: animated(ListItem),
  UnorderedList: animated(UnorderedList),
}

So, the usage becomes this:

homepage.tsx
import { useSpring } from '@react-spring/web'
import { Container } from '@chakra-ui/react'
import { Animated } from '@externals'
 
export const HomePage = () => {
  const springs = useSpring({
    from: {
      y: 0,
    },
    to: {
      y: 30,
    },
  })
 
  return (
    <Container>
      <Animated.Box
        boxSize="200px"
        background="blue"
        borderRadius="8px"
        style={{ ...springs }}
      >
        <Text>Animated Chakra Box</Text>
      </Animated.Box>
    </Container>
  )
}

That's all! Try it out and let me know if it worked. You can share this article too, if you found it helpful, so others can find it too.