Have you seen the chat heads created by Facebook Messenger? They are stacked over each other and when you drag the top one, the other heads follow.

But this happens with leaving a trail. So, it looks like Flash is running fast.

In this article we are going to implement Facebook messenger chat heads using React Spring library.

Let me give you a brief introduction to React Spring. It is a library that can help you in creating your animations. It is simple, powerful, and very optimized. For almost all the general use cases it has sufficient support.

Facebook messenger chat heads using React spring

First, we will make an array of images that we are going to use in chat heads. Let’s call this file, imgs.js.

Code for imgs.js

export default [
  'https://randomuser.me/api/portraits/med/women/22.jpg',
  'https://randomuser.me/api/portraits/med/men/25.jpg',
  'https://randomuser.me/api/portraits/med/men/18.jpg',
  'https://randomuser.me/api/portraits/med/women/55.jpg',
  'https://randomuser.me/api/portraits/med/men/32.jpg',
  'https://randomuser.me/api/portraits/med/women/28.jpg',
  'https://randomuser.me/api/portraits/med/women/73.jpg'
]

These are 7 fake images generated by randomuser.me.

Code for index.js

This is the main file where all the code is going to be. So, we will break it into parts and understand it.

Let’s start with creating a single chat head

<animated.div
>
  <div
	style={{
	  backgroundImage: `url(/*image url*/)`
	}}
  />
</animated.div>

We need to style it a bit so that it can look like a chat head of messenger. Create a stylesheet with name styles.css. Code for stylesheet is

body,
html {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir;
  background: #f0f0f0;
  height: 100vh;
}

* {
  box-sizing: border-box;
  user-select: none;
  -moz-user-select: none;
}

#root {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  width: 100vw;
}

#root > div {
  position: absolute;
  width: 80px;
  height: 80px;
  background: grey;
  border-radius: 50%;
  box-shadow: 0px 10px 30px -5px rgba(0, 0, 0, 0.3);
  transition: box-shadow 0.5s, opacity 0.5s;
  will-change: transform;
  cursor: grab;
  overflow: hidden;
  touch-action: none;
  will-change: transform;
}

#root > div > * {
  height: 100%;
  background-size: cover;
  background-position: center center;
  margin: 0vw 0;
}

#root > div.dragging {
  cursor: grabbing;
}

#root > div.disabled {
  opacity: 0.5;
  cursor: default;
}

#root > div:hover {
  box-shadow: 0px 30px 100px -10px rgba(0, 0, 0, 0.4);
}

button {
  position: absolute;
  top: 20px;
  left: 20px;
}

With the help of this css, the images will become circular and there will be shadow on them. Also they will form a stack due to absolute positioning.

Let’s see the entire index.js file –

import React from 'react'
import ReactDOM from 'react-dom'
import { useTrail, animated } from 'react-spring'
import { useGesture } from 'react-use-gesture'
import imgs from './imgs'
import './styles.css'

function Card() {
  const domTarget = React.useRef(null)

  const trans = (x, y) => `translate3d(${x}px,${y}px,0) translate3d(-50%,-50%,0)`
  const [trail, set] = useTrail(7, () => ({ xy: [0, 0], config: (i) => ({ tension: 1200, friction: 80 }) }))

  const [drag, setDrag] = React.useState(false)

  useGesture(
    {
      onDragStart: () => setDrag(true),
      onDrag: ({ offset: [x, y] }) => set({ xy: [x, y] }),
      onDragEnd: () => setDrag(false)
    },
    { domTarget, eventOptions: { passive: false } }
  )

  return trail.map((props, i) => (
    <animated.div
      key={i}
      ref={i === 0 ? domTarget : null}
      className={`${drag ? 'dragging' : ''}`}
      style={{ transform: props.xy.to(trans), zIndex: 10 - i }}
    >
      <div
        style={{
          backgroundImage: `url(${imgs[i]})`
        }}
      />
    </animated.div>
  ))
}

ReactDOM.render(<Card />, document.getElementById('root'))

For creating draggable Facebook messenger kind of chat heads (with trails), we will need react-spring and react-use-gesture.

  • React Spring – It is used for animation.
  • React use gesture – It is used for implementing dragging functionality.

You should know few things about our process –

  • Only the top image on the stack of chat heads is draggable. Others will just follow the first.
  • We need to set the z-index in decreasing order. Otherwise we will get an unexpected output.

In the code, we are first defining a reference variable, domTarget. We will use it to refer the first chat head for dragging purpose. If you want to learn more about reference variables then have a look at below article –

For creating a trail animation, React spring provides useTrail() hook. We need to provide the total number of trail animations as first parameter. We have 7 images so we will create 7 animations. Second parameter is the function which returns CSS transition values.

To create draggable feature, we are using useGesture hook from react-use-gesture. This will set the transition values according to dragging.

Next, we are creating chat heads by looping over all the 7 trails. Rest, the library will handle.