## Aim

This tutorial aims to guide you on how you can create and add a live avatar
stack to your product using React, Firebase, and Liveblocks. By the end of this
tutorial, you should have a solid understanding of:

- What a live avatar stack is
- What Liveblocks is
- How to install and connect to Liveblocks
- How to use Firebase in Liveblocks for authentication
- A few Liveblocks React hooks such as `useOthers()` and `useSelf()`

## Prerequisites

In this guide, we assume that you already have a decent understanding of:

- [React](https://reactjs.org/) and React Hooks,
- Firebase and
  [Firebase functions](https://firebase.google.com/docs/functions/get-started),
- Javascript Async functions and Higher order functions such as array `map()`
  function.
- Basic Node.js and [npm](https://docs.npmjs.com/getting-started) for you to
  follow along easily.

Also, you need to create a [Liveblocks account](/auth/signup), sign in to your
dashboard and retrieve your [API key](/dashboard/apikeys) (secret key) which
will be used in this tutorial.

## What a live avatar stack is

A live avatar is a pictorial representation of a collaborator working in a
virtual space. Applications like Figma and Dropbox use live avatars to represent
active users. When the user enters the virtual space, an avatar is created to
indicate the user’s presence, and also when the user leaves, the avatar is
removed or dimmed to indicate the user’s absence. This helps collaborators to
work as though they were in a physical room and can see who is present or not.
An avatar stack is a group of avatars and here’s an image of what a typical
avatar stack looks like:

<Figure>
  <Image
    src="/images/blog/avatar-stack-01.png"
    alt="Illustration of an avatar stack"
    width={730}
    height={260}
    quality={90}
  />
</Figure>

## Add Liveblocks to your project

To add Liveblocks to your React project, you first need to install Liveblocks
packages into your project. To do that please run the following command:

```bash
npm install @liveblocks/client @liveblocks/react
```

[`@liveblocks/client`](/docs/api-reference/liveblocks-client) lets you connect
to Liveblocks servers.
[`@liveblocks/react`](/docs/api-reference/liveblocks-react) contains react
utilities to make it easier to consume `@liveblocks/client`.

### Connect to Liveblocks server

Next, you need to connect to Liveblocks server using the `LiveblocksProvider`
component in the root of your project (let’s say `index.js` file) and pass in
`client` as a prop to this component. Remember this makes Liveblocks hooks
available to all of your components wrapped in the `LiveblocksProvider`
component. This is a typical example of what your root file (say `index.js`
file) should look like:

```js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

import { createClient } from "@liveblocks/client";
import { LiveblocksProvider } from "@liveblocks/react";

import firebase from "firebase";
import "firebase/functions";

firebase.initializeApp({
  /* Firebase config */
});

// We will be creating this auth endpoint function later in the tutorial
const auth = firebase.functions().httpsCallable("auth");

// Create a Liveblocks client
const client = createClient({
  authEndpoint: async (room) => (await auth({ room })).data,
});

ReactDOM.render(
  <React.StrictMode>
    <LiveblocksProvider client={client}>
      <App />
    </LiveblocksProvider>
  </React.StrictMode>,
  document.getElementById("root")
);
```

### Connect to Liveblocks room

Remember a room (the virtual space) where users collaborate? The next step is to
connect to this room and to do this, we use the `RoomProvider` in our `App.js`
component and set a "live-avatars" ID like so:

```js
import React from "react";
import { RoomProvider } from "@liveblocks/react";
import AvatarStack from "./AvatarStack.js";

export default function App() {
  return (
    <RoomProvider id="live-avatars">
      {/* We'll be creating this AvatarStack component later in the tutorial */}
      <AvatarStack />
    </RoomProvider>
  );
}
```

## Set-up authentication endpoint using Firebase

Remember you can make use of your Liveblocks public key or an authentication
endpoint to create a client using the `createClient` function. We will be
creating an authEndpoint using Firebase. We assume that you have set up your
Firebase config and added Firebase to your project. Users can only interact with
rooms they have access to. You can control permission access from an http
endpoint by creating a
[Firebase function](https://firebase.google.com/docs/functions/get-started). To
do that, let’s first install `@liveblocks/node` package in your Firebase
functions project.

```bash
npm install @liveblocks/node
```

Next, create a new Firebase
[callable function](https://firebase.google.com/docs/functions/callable) (let’s
say `auth`) as described below. This is where you have to implement your
security and define if the current user has access to a specific room. Don’t
forget to replace `sk_prod_xxxxxxxxxxxxxxxxxxxx` with
[your API key](/dashboard/apikeys).

```js
 const functions = require("firebase-functions");
  const { authorize } = require("@liveblocks/node");

  exports.auth = functions.https.onCall((data, context) => {
    /*
    * Implement your own security here, use the conditional checks below as a guide.
    *
    * It's your responsibility to ensure that the caller of this endpoint
    * is a valid user by validating the cookies or authentication headers
    * and that it has access to the requested room.
    */

    //check if user is authenticated
    if (!context.auth) {
      // Throwing an HttpsError so that the client gets the error details.
      throw new functions.https.HttpsError('The function must be called while authenticated.');
    }

    //check if user has access to the room
    if (room === "live-avatars") {
      // Authentication/user information is automatically added to the request.
      const name = context.auth.token.name || null;
      const picture = context.auth.token.picture || null;

      return authorize({
        room: data.room,
        secret: "{{SECRET_KEY}}" //pass in your Liveblocks secret here
        user: {name, picture} //this is where we get the name and picture of users for our avatars
      }).then((authResult) => {
        if (authResult.status !== 200) {
          throw new functions.https.HttpsError(authResult.status, authResult.body);
        }
        return JSON.parse(authResult.body);
      });
    }
  });
```

We’ve successfully set up our Firebase authentication endpoint!

## Create a live avatar stack UI

Here’s the representation of what our avatar stack structure would look like:

<Figure>
  <Image
    src="/images/blog/avatar-stack-02.png"
    alt="Illustration of an avatar stack"
    width={730}
    height={260}
    quality={90}
  />
</Figure>

### Avatar component

This is what our `Avatar.js` component should look like:

```js file="Avatar.js"
import React from "react";
import styles from "./Avatar.module.css";

export default function Avatar({ picture, name }) {
  return (
    <div className={styles.avatar}>
      <img
        src={picture}
        alt={name}
        height={32}
        width={32}
        className={styles.avatar__picture}
      />
    </div>
  );
}
```

And here is the `Avatar.module.css` file to go alongside with it:

```css file="Avatar.module.css"
.avatar {
  width: 32px;
  height: 32px;
  display: flex;
  justify-content: center;
  align-items: center;
  border-width: 4px;
  border-radius: 32px;
  border-color: #ffffff;
  background-color: #cfd3da;
}

.avatar_picture {
  border-radius: 32px;
}
```

### AvatarStack component

This is what our `AvatarStack.js` component should look like:

```js file="AvatarStack.js"
import { useOthers, useSelf } from "@liveblocks/react";
import React from "react";
import Avatar from "./Avatar.js";
import styles from "./AvatarStack.module.css";

export default function AvatarStack() {
  const users = useOthers().toArray(); //returns all users in the room
  const currentUser = useSelf(); //returns current user in the room
  const hasMoreUsers = users.length > 3;

  return (
    <div className={styles.avatar_stack}>
      <div className={styles.avatar_stack__others}>
        {users.slice(0, 3).map(({ connectionId, user }) => {
          return (
            <Avatar
              key={connectionId}
              picture={user.picture}
              name={user.name}
            />
          );
        })}
        {hasMoreUsers && (
          <div className={styles.avatar_more}>+{users.length - 3}</div>
        )}
      </div>

      {currentUser && <Avatar picture={currentUser.user.picture} name="You" />}
    </div>
  );
}
```

And here is the `AvatarStack.module.css` file to go alongside with it:

```css file="AvatarStack.module.css"
.avatar_stack {
  display: flex;
  flex-direction: row;
}

.avatar_stack > * {
  margin-left: -8px;
}

.avatar_more {
  width: 32px;
  height: 32px;
  display: flex;
  justify-content: center;
  align-items: center;
  border-width: 4px;
  border-radius: 32px;
  border-color: #ffffff;
  background-color: #cfd3da;
  color: #000000;
}
```

## Conclusion

And there you have it! Your `AvatarStack` component is available to be imported
and used however and wherever you wish to use it in your product.

Liveblocks makes it easy to implement online/virtual presence to improve
collaboration between team members. However, a live avatar stack isn’t the only
presence UI pattern you can build with Liveblocks, you can also build things
like live cursors, live selection, and more. Thank you for your time!