Customizing an avatar with React
We'll create a react project to customize a profile avatar image. This avatar will be rendered as a card, and we will be able to customize some graphical aspects such as the pose and expressions with a basic form.We are going to use the avatar-generator API. This API receives query parameters to return a specific character. You can see the final result here.
Let's begin.
First, let's analyze the requirements to render an avatar.
To request an avatar, we just need to get from this base url https://avataaars.io/?avatarStyle=Transparent
then we send query parameters
to set the avatar properties. For example:
- To set the hair style:
topType=value
- To set the hair color:
hairColor=value
- To set the clothing type:
clotheType=value
- To set the eye type:
eyeType=value
- To set the mouth type:
mouthType=value
Now, let's build our setting card.
First, we build our form.js
component:
1import { useEffect, useState } from 'react'; 2 3const Form = (props) => { 4 5 const [form, setForm] = useState({ 6 topType: 'ShortHairShortFlat', 7 hairColor: 'Black', 8 eyeType: 'Default', 9 mouthType: 'Default', 10 clotheType: 'Hoodie', 11 }); 12 13 return <p>form</p>; 14} 15 16export default Form; 17 18
As you can see in the code above, we define a state to manage the form values, but you could use
useRef
or a hook to manage them.
Every state key should correspond to the queryParams specifications for the avatar in order to match the values in advance.
Now, let's return the JSX code:
1 return ( 2 <form className={styles.form}> 3 <div className={styles.group}> 4 <label>Hair</label> 5 <select 6 className={styles.selector} 7 onChange={changeHandler} 8 name='topType' 9 value={form.topType} 10 > 11 <option value='ShortHairTheCaesar'>Short</option> 12 <option value='ShortHairDreads01'>Medium</option> 13 <option value='LongHairBigHair'>Long</option> 14 </select> 15 </div> 16 <div className={styles.group}> 17 <label>Hair color</label> 18 <select 19 className={styles.selector} 20 onChange={changeHandler} 21 name='hairColor' 22 value={form.hairColor} 23 > 24 <option value='Black'>Black</option> 25 <option value='Blonde'>Blonde</option> 26 <option value='Red'>Red</option> 27 </select> 28 </div> 29 <div className={styles.group}> 30 <label>Eyes</label> 31 <select 32 className={styles.selector} 33 onChange={changeHandler} 34 name='eyeType' 35 value={form.eyeType} 36 > 37 <option value='Default'>Neutral</option> 38 <option value='Close'>Close</option> 39 <option value='Wink'>Wink</option> 40 </select> 41 </div> 42 <div className={styles.group}> 43 <label>Mouth</label> 44 <select 45 className={styles.selector} 46 onChange={changeHandler} 47 name='mouthType' 48 value={form.mouthType} 49 > 50 <option value='Smile'>Smile</option> 51 <option value='Serious'>Serious</option> 52 <option value='Default'>Neutral</option> 53 </select> 54 </div> 55 <div className={styles.group}> 56 <label>Clothes</label> 57 <select 58 className={styles.selector} 59 onChange={changeHandler} 60 name='clotheType' 61 value={form.clotheType} 62 > 63 <option value='Hoodie'>Hoodie</option> 64 <option value='ShirtCrewNeck'>Shirt</option> 65 <option value='BlazerSweater'>Blazer</option> 66 </select> 67 </div> 68 </form> 69 ); 70
As you see, I'm repeating the select inputs and matching the values with the state keys. It's better to use an isolated component to manage the select functionality, but in this basic example it's OK.
Now, let's create the changeHandler
method
1const changeHandler = (evt) => { 2 setForm(current => { 3 return { 4 ...current, 5 [evt.target.name]: evt.target.value 6 } 7 }); 8}; 9
The spread operator is used to update the specific key state but without deleting the other keys. This is an approach to managing forms in React
Finally, let's communicate these state changes to the parent component through props:
1useEffect(() => { 2 props.onUpdate(form); 3}, [form, props]); 4
We need to use useEffect
hook to emit every state change.
Now let´s work on the main avatar.js
component:
1import { useState } from 'react'; 2import styles from './avatar.module.css'; 3import Form from './form'; 4 5const Avatar = () => { 6 7 const updateAvatarHandler = (queries) => { 8 // set avatar 9 } 10 11 return ( 12 <div className={styles.card}> 13 <img 14 className={styles.image} 15 alt='avatar' 16 src='' 17 /> 18 <Form 19 onUpdate={updateAvatarHandler} 20 /> 21 </div> 22 ); 23}; 24 25export default Avatar; 26
This component is pretty simple. We just rendered a card with an image tag and the form.js
component.
So far, it looks like this:
Finally, let's add the logic to render the avatar.
First, let's add an initial avatar, creating a default url:
1const defaultAvatar = `https://avataaars.io/?avatarStyle=Transparent&topType=ShortHairShortFlat&hairColor=Black&clotheType=Hoodie&eyeType=Default&mouthType=Default`; 2 3
And, we should create a state to manage the image src:
1 const [avatar, setAvatar] = useState(defaultAvatar); 2
Then, in the <img />
tag:
1<img 2 className={styles.image} 3 alt='avatar' 4 src={avatar} 5/> 6
Now, the card should look like this:
To finish, let's add some logic in the updateAvatarHandler
method:
1const updateAvatarHandler = (queries) => { 2 const newQueryArr = Object.entries(queries).map((query) => { 3 return `${query[0]}=${query[1]}`; 4 }); 5 setAvatar(`https://avataaars.io/?avatarStyle=Transparent&clotheColor=Blue03&${newQueryArr.join('&')}`); 6} 7
In this method, we are receiving an object with the keys and values needed to set the avatar through queryParams: So we just need to use Object.entries
to get an array and the .map()
method to evaluate the values and create a new array for each queryParam.
To finish, we call setAvatar
method to update the src element in the image, and, using the .join method in the new array, we convert the array to a string separated by the &
symbol in order to separate the different queryParams.
That's all. The final app should work like this:
Summary
In this project, we learned how to create a basic application to customize an avatar using React and avatar-generator
You can find the code here