/    Sign up×
Articles /Pin to ProfileBookmark

How I built avatars in Lemon Squeezy using Vue

It might not seem very difficult or complicated but getting avatars right can be harder than it first appears. This is usually because there are multiple different locations avatars can come from. In Lemon Squeezy, we try and show a user avatar based on multiple locations in the following order:

  1. A custom avatar that has been uploaded by the user
  2. The user’s Gravatar based on their email address
  3. A fallback avatar with the initials of the user’s name

To achieve this, let’s assume each user has three pieces of data:

  1. A user_name
  2. An avatar_url (uploaded avatar or Gravatar)
  3. A user_color (randomly generated)

The Avatar component

Here’s what our Avatar component looks like in Vue:

<template>
    <div class="relative w-full h-full">
        <SvgAvatar
            :text="user_name"
            :bg-color="user_color"
            class="absolute inset-0 w-full h-full"
        />
        <img
            v-if="avatar_url"
            :src="avatar_url"
            alt=""
            class="absolute inset-0 block w-full h-full rounded-full"
        />
    </div>
</template>

<script>
export default {
    props: {
        user_name: {
            type: String,
            required: true,
        },
        user_color: {
            type: String,
            default: '#7047EB',
        },
        avatar_url: {
            type: String,
            default: '',
        },
    },
};
</script>

Data is passed to the component as props. We then use another component called SvgAvatar which generates the fallback avatar (colored circle with the user’s initials). Finally, we display the image avatar (custom or Gravatar) if it exists.

We absolutely position both of these elements so that the image is on top of the SvgAvatar. Why? Well, we always want to show the image avatar if one exists. But what if the user has no custom avatar and has no Gravatar image set? The user will still have a Gravatar URL generated for their email address but we don’t just want to show the default blank Gravatar. We want to show our own fallback SvgAvatar instead. To get around this, we set the Gravatar default image to “blank” so that it serves a transparent PNG which allows the SvgAvatar positioned behind it to be seen. This is how we generate the Gravatar URL in the backend:

'https://www.gravatar.com/avatar/' . md5($user->email) . '?d=blank';

Note the d=blank. Technically we could set the default image to be a fallback image URL that we have generated but I wanted to use SVGs for this and Gravatar only supports images.

The SvgAvatar component

The SvgAvatar component looks like this:

<template>
    <div v-if="svg" class="svg-avatar" v-html="svg" />
</template>

<script>
import UIAvatarSvg from 'ui-avatar-svg';

export default {
    props: {
        text: {
            type: String,
            required: true,
        },
        bgColor: {
            type: String,
            default: '#ff0000',
        },
        textColor: {
            type: String,
            default: '#ffffff',
        },
    },

    data() {
        return {
            svg: null,
        };
    },

    mounted() {
        this.svg = new UIAvatarSvg()
            .text(this.text.charAt(0).toUpperCase())
            .bgColor(this.bgColor)
            .textColor(this.textColor)
            .fontSize(0.6)
            .fontWeight(600)
            .fontFamily(
                "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'"
            )
            .generate();
    },
};
</script>

I use a small library I built called UI Avatar SVG to generate the svgs. Here we simply pass the user_name and user_color as the text and bgColor props and use this data to generate the svg fallback avatar. The svg is then output to the div element using Vue’s v-html directive.

And that’s how our avatars work in Lemon Squeezy.

Front-endJavaScriptVue.js
×

Success!

Help @gilbitron spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 4.26,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,

tipper: @Samric24,
tipped: article
amount: 1000 SATS,
)...