1 line of code to animate element additions, removals and moves

Published on in JavaScript, Mithril.js and React

AutoAnimate is a JS library that provides simple animations with no effort. Works with Mithril.js, React and any other JS app.

Table of contents

Highlights

React.js example

From AutoAnimate's website:

import { useAutoAnimate } from '@formkit/auto-animate/react'

function MyList() {
  const [animationParent] = useAutoAnimate()
  return <ul ref={animationParent}>{/* 🪄 Magic animations for your list */}</ul>
}

If the import statement is not counted, that's only two lines of code. Nice!

(The Mithril.js example below works with only one line of code.)

Empty <ul> is problematic

If you don't want to render an empty <ul> element, you need to use AutoAnimate on the <ul>'s parent element too:

import { useAutoAnimate } from '@formkit/auto-animate/react'

function MyList({ items }) {
  const [parent] = useAutoAnimate()
  const [list] = useAutoAnimate()

  return (
    <div ref={parent}>
      {items.length > 0 && <ul ref={list}>{/* Render items */}</ul>}
    </div>
  )
}

But there's currently an open React-specific GitHub issue that AutoAnimate doesn't work with containers that are not initially rendered.

Manual exit animations would be difficult

Animating the removal of an element is tricky in React because when React removes an element from the DOM, the element is removed immediately. There's no time to run CSS animations.

If I interpreted AutoAnimate's source code correctly, it animates element removal like this:

  1. React (or whatever you're using) removes an element from the DOM.
  2. AutoAnimate adds the element back to the DOM!
  3. AutoAnimate triggers an animation for the element.
  4. AutoAnimate removes the element from the DOM after the animation is finished.

Sounds a bit hacky and risky to modify a React-rendered DOM like this, but seems to work fine. Quite clever actually!

Mithril.js example

Mithril.js is so cool that you only need one line of code:

import autoAnimate from '@formkit/auto-animate'

function MyList() {
  return {
    view: () =>
      m('ul', { oncreate: (vnode) => autoAnimate(vnode.dom) }, [
        // Render items
      ]),
  }
}

The oncreate lifecycle hook in Mithril.js is like the good old componentDidMount lifecycle method in React.

vnode.dom points to the DOM element, so it's like a ref in React.

In Mithril.js, components and virtual DOM nodes have lifecycle hooks and "automatic" refs (no need for something like createRef or useRef). Super useful.

Empty <ul> is no problem

Like with React, you'll still need to use AutoAnimate on the <ul>'s parent element too if you don't want to render an empty <ul>:

import autoAnimate from '@formkit/auto-animate'

function MyList() {
  return {
    view: ({ attrs: { items } }) =>
      m('div', { oncreate: (vnode) => autoAnimate(vnode.dom) }, [
        items.length > 0 &&
          m('ul', { oncreate: (vnode) => autoAnimate(vnode.dom) }, [
            // Render items
          ]),
      ]),
  }
}

But the aforementioned React-specific issue is not a problem with Mithril.js, things just work. Did I say that Mithril.js is cool!

Manual exit animations would be... easy!

The onbeforeremove lifecycle hook in Mithril.js makes implementing exit animations easy. From the docs (code example slightly edited by me):

The onbeforeremove(vnode) hook is called before a DOM element is detached from the document. If a Promise is returned, Mithril.js only detaches the DOM element after the promise completes. [...]

const Fader = {
  onbeforeremove(vnode) {
    vnode.dom.classList.add('fade-out')
    return new Promise((resolve) => setTimeout(resolve, 1000))
  },
  view: () => m('div', 'Bye'),
}

AutoAnimate still has its place though – it's great for adding quick and easy animations with no effort.