loading...

styled-component + react-transition-group = very simple Transition

terrierscript profile image terrierscript Updated on ・2 min read

If we need animate react component, we can use library like react-pose or react-spring. Those library is very cool but so heavy if we need only tiny transition.

On the other hand, react-transition-group is so simple.

If we use styled-componets, <Transition> component may be better than <CSSTransition>

Example

First, I create transition wrapped component.
In this example, I use React Hooks but you can use class component if you need.

import { Transition } from "react-transition-group"
import { Animation } from "./Animation"

export const AnimateItem = () => {
  const [animate, setAnimate] = useState(false)

  // Animate on click button and revert after 3000ms.
  const doAnimate = useCallback(() => {
    setAnimate(true)
    setTimeout(() => {
      setAnimate(false)
    }, 3000)
  }, [])

  return (
    <div>
      {/* Transition change state with `in` props */}
      <Transition in={animate} timeout={500}>
        {(state) => (
          // state change: exited -> entering -> entered -> exiting -> exited
          <Animation state={state}>Hello</Animation>
        )}
      </Transition>
      <button onClick={doAnimate}>Animate</button>
    </div>
  )
}

Next, create component that based on styled-component.

// Animation.js
import styled from "styled-components"

export const Animation = styled.div`
  transition: 0.5s;
  width: 300px;
  height: 200px;
  /* example for move item */
  transform: translateX(
    ${({ state }) => (state === "entering" || state === "entered" ? 400 : 0)}px
  );
  /* change color*/
  background: ${({ state }) => {
    switch (state) {
      case "entering":
        return "red"
      case "entered":
        return "blue"
      case "exiting":
        return "green"
      case "exited":
        return "yellow"
    }
  }};
`

You can split Item and Animation too.

const BaseItem = styled.div`
  width: 300px;
  height: 200px;
`

export const Animation = styled(BaseItem)`
  transition: 0.5s;
  transform: translateX(
    ${({ state }) => (state === "entering" || state === "entered" ? 400 : 0)}px
  );
`

Preview

preview

Fade example

You can create fadeIn/fadeOut animation with same way.

export const Fade = styled.div`
  transition: 0.5s;
  opacity: ${({ state }) => (state === "entered" ? 1 : 0)};
  display: ${({ state }) => (state === "exited" ? "none" : "block")};
`

Or you can use with unmountOnExit and mountOnEnter.


export const Fade2 = styled.div`
  transition: 5s;
  opacity: ${({ state }) => (state === "entered" ? 1 : 0)};
`

const Item = () => {
  // ...
  return <Transition in={animate} timeout={500} unmountOnExit mountOnEnter>
    {(state) => <Fade2 state={state}>Fade In</Fade2>}
  </Transition>
}

Discussion

markdown guide
 

Thanks for this guide! It was helpful!

Not sure if you can give some pointers, but I have this scroll up and down button that'll only appear when there's "space" to scroll, if it's at the end of the scroll position, it'll hide the respective button (e.g: at the top; hides up button and shows down button, at the bottom; hides down button and shows up button, in the middle; shows both buttons)

I managed to make it work, but I hit a snag. Can't seem to figure out how to make it on first load/page visit, to hide the up button. It works as expected once you scroll down the page and up, it's only the first time it loads, the up button needs to be hidden. Thoughts/suggestions?

 

THANK YOU!!! After hours of trying I found this article and it saved my whole day.

 

Is the useCallback() necessary? What's different if it is omitted?

 

No, it's optional.

This post is help for understand useCallback
kentcdodds.com/blog/usememo-and-us...

 

Thanks, this is a great resource! Any idea how to make it animate on mount? I see the docs mention that you should add an "appear" prop to "Transition" but it had no effect for me...