Pratik Sharma
Coolhead

Coolhead

How to create Avatar Maker in Next JS and React ?

Pratik Sharma
·Jul 21, 2022·

7 min read

How to create Avatar Maker in  Next JS and React ?

Photo by Shubham Dhage on Unsplash

Subscribe to my newsletter and never miss my upcoming articles

Play this article

Things that you will learn in this tutorial :

  • We will be making an avatar maker or avatar generator from Scratch.
  • Designing our avatar in SVG and building variant elements that can be changed by the user.
  • Building a client-side React application that will serve the purpose of Avatar builder.
  • Sending SVG from the Server, or using API to create SVG which you can use as a source in your image tags.

Git repo here: https://github.com/biomathcode/avatarmaker


Here is what it looks like:

finale-image (1).gif

1. Designing your avatar

You can use any application that can export an illustration in SVG. We will build elements and their variants that can be changed by the user.

I will be building the emoticon Avatar, which is simple and easy. You can increase the complexity based on your illustration or drawing skills.

I will use Figma.

Here is the final Figma File:

https://www.figma.com/file/VB1vWRMmMOskDxc5WAYWCt/AvatarMaker?node-id=0%3A1

Step to create an Emoticon

a. Draw an ellipse - head

b. Draw two more small ellipses - eyes

c. Draw a middle ellipse - mouth

face1.png

Similarly, You can change the mouth ellipse shape with other shapes, to create more variants for an emoticon.

These are our “Elements”

AvatarVariants.png

Figma Tips: Creating a component with multiple variants.

  1. Select your element and left-click to create a component ( options + command + k on mac)
  2. Name the element as, ElementName/Property

    In our case, We will name it Face/emotionType Screenshot 2022-07-21 at 7.53.28 PM.png

c. Select all Variants and click Merge as variants

chrome-capture-2022-6-21 (1).gif

2. Creating BoilerPlate for Nextjs.

Run in the terminal. I have added the typescript flag. not necessary though.

mkdir avatermaker
cd avatermaker 
npx create-next-app --ts .

Let’s create a component folder, where we will have our Icons.tsx components.

mkdir components

3. SVG to JSX

visit: https://svg2jsx.com/ and convert your SVG to JSX. 🤯

Now, if you look head and the eyes for the SVG remain the same. The mouth creates different emotion variants for our emoticons.

We will create a data JSON with your different emotions variant names as keys and their shape or path as value:

const data = {
  Shocked: <circle cx="63" cy="92" r="21" fill="#333"></circle>,

  Confused: <path fill="#333" d="M83 92a21.001 21.001 0 00-42 0h42z"></path>,
  Anxious: (
    <path
      stroke="#333"
      strokeLinecap="round"
      strokeWidth="4"
      d="M47 82L80 82"
    ></path>
  ),
  Smile: (
    <path
      stroke="#333"
      strokeLinecap="round"
      strokeWidth="4"
      d="M39 79c15.5 15.5 31 14 46-1"
    ></path>
  ),
};

Our Icon component will have one prop, which will determine which emotion the emoticon will show.

type Emotion = 'Smile' | 'Anxious' | 'Shocked' | 'Confused'

export function Icon({ emotion }: {
  emotion: Emotion
}  ) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width="124"
      height="124"
      fill="none"
      viewBox="0 0 124 124"
    >
      <circle cx="62" cy="62" r="62" fill="#E4D653"></circle>
      <ellipse cx="37" cy="44.5" fill="#333" rx="10" ry="10.5"></ellipse>
      <ellipse cx="86" cy="44.5" fill="#333" rx="10" ry="10.5"></ellipse>
      {data[emotion]}
    </svg>
  );
}

4. State Management and Styles

Here are some simple helper classNames that will be enough for us. Add this to your styles/global.css file

html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
    background: #1e1e1e;
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}

.flex {
  display: flex;
}
.center {
  align-items: center;
  align-content: center;
}

.gap-10 {
  gap: 10px;
}

.white {
  color: white;
}

.se {
  justify-content: space-evenly;
}

.btn {
  border: none;
  background: #1e1e1e;
  cursor: pointer;
}

.btn:hover {
  background: #101010;
}

.mt-10 {
  margin-top: 10px;
}

.mt-30 {
  margin-top: 30px;
}

.blue {
  color: lightblue;
}

Add RIghtArrow and LeftArrow SVG in the components/Icons.tsx file, which we will use to toggle our emoticon variants.

export function RightArrow() {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width="32"
      height="38"
      fill="none"
      viewBox="0 0 32 38"
    >
      <path
        fill="#D9D9D9"
        d="M29.237 15.631c2.458 1.573 2.458 5.165 0 6.738L6.157 37.143C3.493 38.847 0 36.935 0 33.773V4.227C0 1.065 3.494-.847 6.156.857l23.08 14.774z"
      ></path>
    </svg>
  );
}

export function LeftArrow() {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width="32"
      height="39"
      fill="none"
      viewBox="0 0 32 39"
    >
      <path
        fill="#D9D9D9"
        d="M2.263 16.131c-2.458 1.573-2.458 5.165 0 6.738l23.08 14.774c2.663 1.704 6.157-.208 6.157-3.37V4.727c0-3.161-3.494-5.073-6.157-3.369L2.264 16.131z"
      ></path>
    </svg>
  );
}

Now, let's add state management and simple toggle buttons .

import type { NextPage } from "next";
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { Icon, LeftArrow, RightArrow } from "../components/Icons";
import { useState } from "react";

export type Emotion = "Smile" | "Anxious" | "Shocked" | "Confused";

const EmotionTypes: Emotion[] = ["Smile", "Anxious", "Shocked", "Confused"];

const Home: NextPage = () => {
  const [state, setState] = useState(0);

//increase state by 1 and if state equals the length of the array, reset state to 0;
  const increase = () => {
    state === Number(EmotionTypes.length - 1)
      ? setState(0)
      : setState((e) => e + 1);
  };

// decrease state by 1 if the state becomes negative, reset state to a max of the array. 
  const decrease = () => {
    state <= 0
      ? setState(Number(EmotionTypes.length - 1))
      : setState((e) => e - 1);
  };

  return (
    <div className={styles.container}>
      <Head>
        <title>
          How to create Avatar Maker or Avatar Generator with React || Nextjs
        </title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <div className="flex se center gap-10">
          <button className="btn" onClick={() => decrease()}>
            <LeftArrow />
          </button>
          <Icon emotion={EmotionTypes[state]} />
          <button className="btn" onClick={() => increase()}>
            <RightArrow />
          </button>
        </div>
        <div className="white mt-10">{EmotionTypes[state]}</div>
      </main>
    </div>
  );
};

export default Home;

5. Setting SVG over the Nextjs API

Our API route api/hello?emo={emotion} will send SVG, here is how it will work

Similarly, we will have a data object that holds information about emotion variants. Make sure that here you using SVG attributes and not JSX attributes for SVG. In our case, change

strokeWidth to stroke-width

strokeLinecap to stroke-linecap

    type Data = string;

    const data = {
      Shocked: '  <circle cx="63" cy="92" r="21" fill="#333"></circle> ',

      Confused:
        ' <path fill="#333" d="M83 92a21.001 21.001 0 00-42 0h42z"></path> ',

      Anxious:
        '<path stroke="#333" stroke-linecap="round" stroke-width="4" d="M47 82L80 82"></path> ',

      Smile:
        ' <path stroke="#333" stroke-linecap="round" stroke-width="4" d="M39 79c15.5 15.5 31 14 46-1"></path>',
    };

Now, based on the emo queries value we will send the desired emoticon.

import type { NextApiRequest, NextApiResponse } from "next";

type Data = string;

const data = {...}

type Req = "Shocked" | "Confused" | "Anxious" | "Smile";

export default function handler(
      req: NextApiRequest,
      res: NextApiResponse<Data>
    ) {
      const query = req.query.emo as string;
      const emo = query as Req;
      const emotionSvg: string = data[emo];
      const svgdata = `
      <svg
      xmlns="http://www.w3.org/2000/svg"
      width="124"
      height="124"
      fill="none"
      viewBox="0 0 124 124"
    >
    <circle cx="62" cy="62" r="62" fill="#E4D653"></circle>
    <ellipse cx="37" cy="44.5" fill="#333" rx="10" ry="10.5"></ellipse>
    <ellipse cx="86" cy="44.5" fill="#333" rx="10" ry="10.5"></ellipse>
      ${emotionSvg}

      </svg>
      `;
      res.setHeader("content-type", "image/svg+xml");
      res.status(200).send(svgdata); }

We will add a preview link, where users can route and see their emoticon. The link can also be used by users for adding to other websites.

This is very similar to how BuyMeACoffee Button works or the ko-fi Button. You can also, add your name or other information like the number of social media followers, you have.

You can also, add the link in your GitHub repository readme file as well. Obviously, the link has to be HTTPS.


  const Home: NextPage = () => {

  //state logic

  const link = `http://localhost:3000/api/hello?emo=${EmotionTypes[state]}`;
  return (

      // html
     <a target={"_blank"} 
                  rel="noreferrer"
                href={link} 
                  className="blue mt-30"
     >
            Link Preview
          </a>

  )
  }

Here, is how the complete index.tsx file will look like

import type { NextPage } from "next";
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { Icon, LeftArrow, RightArrow } from "../components/Icons";
import { useState } from "react";

    export type Emotion = "Smile" | "Anxious" | "Shocked" | "Confused";

    const EmotionTypes: Emotion[] = ["Smile", "Anxious", "Shocked", "Confused"];

    const Home: NextPage = () => {
      const [state, setState] = useState(0);

      const increase = () => {
        state === Number(EmotionTypes.length - 1)
          ? setState(0)
          : setState((e) => e + 1);
      };

      const decrease = () => {
        state <= 0
          ? setState(Number(EmotionTypes.length - 1))
          : setState((e) => e - 1);
      };

      const link = `http://localhost:3000/api/hello?emo=${EmotionTypes[state]}`;

      return (
        <div className={styles.container}>
          <Head>
            <title>
              How to create Avatar Maker or Avatar Generator with React || Nextjs
            </title>
            <meta name="description" content="Generated by create next app" />
            <link rel="icon" href="/favicon.ico" />
          </Head>

          <main className={styles.main}>
            <div className="flex se center gap-10">
              <button className="btn" onClick={() => decrease()}>
                <LeftArrow />
              </button>
              <Icon emotion={EmotionTypes[state]} />
              <button className="btn" onClick={() => increase()}>
                <RightArrow />
              </button>
            </div>
            <div className="white mt-10">{EmotionTypes[state]}</div>

            <a
              target={"_blank"}
              rel="noreferrer"
              href={link}
              className="blue mt-30"
            >
              Link Preview
            </a>
          </main>
        </div>
      );
    }; 
  export default Home;

7. Conclusion.

I hope this tutorial helps you create your own avatar maker in Next . Or at least, give you a step-wise process that you can follow, to make your own.

There are many other features that you can add. A few

  • Add more options other than just the mouth change, such as background colour change, head colour change, etc.
  • Add a button to download the Avatar. There are a lot of libraries for that.
  • You can animate SVG as well.

    Okay, let me fade away.

Did you find this article valuable?

Support Pratik Sharma by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this

Impressum

You can connect with me on Twitter | LinkedIn || github