DEV Community

terrierscript
terrierscript

Posted on • Updated on

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

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>
}

Top comments (5)

Collapse
 
wheelhot profile image
Omar Tan • Edited

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?

Collapse
 
mtallerico1 profile image
Mike Tallerico

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

Collapse
 
jasonkylefrank profile image
Jason K Frank

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

Collapse
 
terrierscript profile image
terrierscript

No, it's optional.

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

Collapse
 
shaunsaker profile image
Shaun Saker

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...