Generating Custom Bitmoji in Next.js

Chris Achinga

A Bitmoji is simply an emoji of a person’s resemblance. Users across different social media platforms commonly use them as customized emojis that attempts to replicate a user's physical appearance.

Here is an example of Bitmojis:

This post will cover generating a custom Bitmoji using a Next.js application.

APIs and Platforms for Bitmoji

One of the most common platforms for creating and customizing Bitmoji is https://www.bitmoji.com/. Other platforms are:

  • Facebook Avatar
  • Snapchat
  • Mirror AI
  • Canva Bitmoji

Prerequisites

To fully follow this tutorial, you will need the following:

  • Node.js Installed
  • Mirror AI Account (Mirror AI offers a free subscription for limited API calls)
  • React.js/Next.js

Live Demo and GitHub

You can get the live demo at CodeSandbox, and the source code is available on GitHub.

Mirror AI API

For this post, you will use Mirror AI to generate custom Bitmoji. Mirror AI enables a user to generate Bitmojis and stickers with their face. Users upload an image with their face, and the API transforms it into a Bitmoji.

One must have an account to get the API key to use the API. Use this link to create a Free Account: https://business.mirror-ai.com/pricing. Once the account is successfully created, you will be redirected to an API dashboard page containing API keys and links to documentation.

For this post, you will use the /generate API to upload and get a custom Bitmoji.

To test the /generate API, click the Try it out button on the top right. Copy your API key from your account dashboard. Paste the token inside the X-Token input box:

Inside the Request body container, click the Choose File input to upload an image.

Note: For better results, use an up-close image, i.e.:

After selecting an image, click on the blue Execute button:

The API returns the following after a successful execution:

1{
2 "ok": true,
3 "face": {
4 "id": "C7asFWrkQKC001U_8N55pg",
5 "version": "yB4",
6 "url": "https://f.mirror-ai.net/x/mjmTO_wGOteE7Zlvg9tuj820OaLCMZKcRBXRP7LiiO19QENeSW_1uc1VIIY4LxQJMLALuDzM7CzWD4DA6KYURIkSxNKyb6tRmhdg372x974"
7 }
8 }

The Bitmoji created below was obtained from the url inside the response:

Creating a Next.js Application

To create, open the terminal and run the following:

npx create-next-app bitmoji-generator

Once the installation is complete, run the development server to confirm a successful setup:

npm run dev

For basic styling of the page, you will use bootstrap css. To install bootstrap, run the following in the terminal:

npm i bootstrap@5.2.0

Once the installation is complete, import Bootstrap inside pages/_app.js file:

1import 'bootstrap/dist/css/bootstrap.min.css'
2 import '../styles/globals.css'
3 function MyApp({ Component, pageProps }) {
4 return <Component {...pageProps} />
5 }
6 export default MyApp

Working with the API

Mirror AI is developed for use inside server-side code, but since you are using Next.js, a client-side library, you will have to use a different approach. You will need to install the following modules:

  • multer: A middleware to handle file uploads of type multipart/form-data
  • cors: A Node.js middleware to handle and manage Cross-Origin Resource Sharing(CORS)
  • form-data: A library to be used to submit forms and file uploads to other web applications

Install the modules by running the following command:

npm install multer cors form-data

Inside the pages/api directory, add a new file: mirror.js, then paste the code snippet below:

1import multer from 'multer'
2const axios = require('axios')
3const FormData = require('form-data')
4const fs = require('fs')
5
6export const config = {
7 api: {
8 bodyParser: false,
9 },
10}
11
12const storage = multer.diskStorage({
13 destination: './public/uploads',
14 filename: (req, file, cb) => cb(null, file.originalname),
15})
16
17const upload = multer({
18 storage,
19})
20
21export default function mirror(req, res) {
22 upload.single('file')(req, {}, async (err) => {
23 const filePath = `./public/uploads/${req.file.originalname}`
24 const form = new FormData()
25 form.append(
26 'photo',
27 fs.readFileSync(filePath),
28 `${req.file.filename};type=${req.file.mimetype}`
29 )
30
31 const response = await axios.post(
32 'https://public-api.mirror-ai.net/v2/generate',
33 form,
34 {
35 params: {
36 style: 'kenga',
37 },
38 headers: {
39 ...form.getHeaders(),
40 accept: 'application/json',
41 'X-Token': 'f63272366f3b4d56915b620101a7a2e7',
42 'Content-Type': 'multipart/form-data',
43 },
44 }
45 )
46
47 if (response.data.ok) {
48 res.status(200).json({ msg: response.data })
49 } else {
50 res.status(500).json({ msg: 'error' })
51 }
52 })
53}

In the code above, multer is used to enable file upload and specify the upload directory:

1const storage = multer.diskStorage({
2 destination: './public/uploads',
3 filename: (req, file, cb) => cb(null, file.originalname),
4 })
5 const upload = multer({
6 storage,
7 })

Inside the mirror() function, you use fetch API to send data to mirror ai API:

1const response = await axios.post(
2 'https://public-api.mirror-ai.net/v2/generate',
3 form,
4 {
5 params: {
6 style: 'kenga',
7 },
8 headers: {
9 ...form.getHeaders(),
10 accept: 'application/json',
11 'X-Token': 'f63272366f3b4d56915b620101a7a2e7',
12 'Content-Type': 'multipart/form-data',
13 },
14 }
15 )

Once the upload is successful, you return the response:

1if (response.data.ok) {
2 res.status(200).json({ msg: response.data })
3 } else {
4 res.status(500).json({ msg: 'error' })
5 }

Form: Uploading Image

For a user to upload their image, you will use form input of type that takes in files:

<input type='file' />

Create a new directory called components, add a new file ImageUploadForm.jsx, and add the following snippet below:

1import Image from 'next/image'
2 import { useState } from 'react'
3
4 const ImageUploadForm = () => {
5 // upload image to API
6 const [image, setImage] = useState(null)
7 const [imgResult, setImgResult] = useState(null)
8
9 const handleImageUpload = (e) => {
10 // get file from event
11 setImage(e.target.files[0])
12 }
13 const handleSubmit = (e) => {
14 e.preventDefault()
15 const formData = new FormData()
16 formData.append('file', image)
17 fetch('/api/mirror', {
18 method: 'POST',
19 body: formData,
20 })
21 .then((res) => res.json())
22 .then((res) => setImgResult(res.msg.face.url))
23 .catch((err) => console.log(err))
24 }
25 return (
26 <div>
27 <form className='form form-control' onSubmit={handleSubmit}>
28 <div className='m-4'>
29 <label htmlFor='formFileLg' className='form-label'>
30 Upload Your Photo
31 </label>
32 <input
33 className='form-control form-control-lg'
34 type='file'
35 required
36 onChange={handleImageUpload}
37 />
38 <button className='btn btn-primary mt-3' type='submit'>
39 Submit
40 </button>
41 </div>
42 </form>
43 {imgResult && (
44 <Image alt='bitmoji' width={500} height='500' src={imgResult} />
45 )}
46 </div>
47 )
48 }
49 export default ImageUploadForm

In the snippet above, you’ve used React Hooks to handle image uploads and get the new image from the API:

1const [image, setImage] = useState(null)
2 const [imgResult, setImgResult] = useState(null)

You will have a handleImageUpload() and handleSubmit() to handle events when a user uploads an image and after the image is submited:

1const handleImageUpload = (e) => {
2 // get file from event
3 setImage(e.target.files[0])
4 }
5 const handleSubmit = (e) => {
6 e.preventDefault()
7 const formData = new FormData()
8 formData.append('file', image)
9 fetch('/api/mirror', {
10 method: 'POST',
11 body: formData,
12 })
13 .then((res) => res.json())
14 .then((res) => setImgResult(res.msg.face.url))
15 .catch((err) => console.log(err))
16 }

Run the local server to view the changes:

npm run dev

Try uploading an image with a good view of the face and notice the changes:

Conclusion:

This post demonstrates how Mirror API generates custom Bitmoji.

Resources

  1. https://github.com/netlify/next-react-server-components
  2. https://vercel.com/blog/everything-about-react-server-components
  3. https://mirror-ai.com/
  4. https://rapidapi.com/mirror-dev-team/api/mirror-ai/

Chris Achinga

Technical Writer