How to Implement Name-Based Avatar Component in Vue
Tags: vue
Published: 2022-09-22 19:42:31

One common way to display avatars for users without an image is showing the first letter on a colored circle.

We need to consider the following when creating such a component:
- It should display the image if provided.
- It should show the same background color for the same user name.
- The font size of the letter should be responsive based on the container size.

## The code
I named the component in this example `Avatar.vue`. So, here's how the usage code would be:

```html
<!-- This will show the image version -->
<Avatar name="Test User" image="/image.png" />

<!-- This will show the first letter version -->
<Avatar name="Test User" />
```

In the code below, I'm using `v-bind()` in the css, which is a Vue 3 feature that allows us to bind variables to the css. If you are using Vue 2, make sure to use style binding instead. Like `<div :style="{ backgroundColor: bgColor }></div>`.

Here's the code for `Avatar.vue`:
```html
<template>
  <div ref="containerRef" class="avatar">
    <img v-if="image" :src="image" class="image" :alt="name" />
    <span v-else-if="name" class="initials">
      {{ name[0] }}
    </span>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue'

const props = defineProps({
  image: {
    type: String,
    default: ''
  },

  name: {
    type: String,
    default: ''
  }
})

const containerRef = ref()
const fontSize = ref('1rem')

onMounted(() => {
  // The font size should be 50% of the container width
  fontSize.value = `${containerRef.value.clientWidth * 0.5}px`
})

const bgColor = computed(() => {
  // Don't show a color if the image is provided
  if (props.image) {
    return 'transparent'
  }
  // Calculate the hue value based on the name
  const hue =
    props.name.split('').reduce((acc, cur) => {
      return acc + cur.charCodeAt(0)
    }, 0) % 360
  return `hsla(${hue}, 60%, 50%, 1)`
})
</script>

<style scoped>
.avatar {
  width: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  /* bind the background color to bgColor */
  background: v-bind(bgColor);
}

.image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.initials {
  /* bind the font-size to fontSize */
  font-size: v-bind(fontSize);
  color: white;
  font-family: sans-serif;
  user-select: none;
}
</style>
```

If you pass a specific name to this component, you will always get the same background color for it. It works like this because, in `bgColor` computed property, I'm calculating the color based on the sum of the char code of each letter in the name.

I'm using `hsla` for the color to be able to control the color value apart from the other components, which are saturation and lightness.

For this example, I set the font size to the half of the container's width. So if you want to make it smaller or bigger, just change `0.5` in `fontSize.value = ${containerRef.value.clientWidth * 0.5}px`.

---

If you liked this article, check out [my blog](https://tahazsh.com/) for more articles like this.
I also share coding tips and my latest work on [Twitter](https://twitter.com/tahazsh). Let's be friends!