Components
Avatar
An image element with a fallback for representing the user.
![https://randomuser.me/api/portraits/men/15.jpg](https://randomuser.me/api/portraits/men/15.jpg)
JD
Source Code
Copy the following code into your project.
<template>
<div
:class="
cn(
variants({ size, shape, class: props.class }),
'relative inline-flex items-center justify-center'
)
"
>
<!-- Display image is src was passed -->
<img
:class="cn(variants({ shape }), 'inline-block h-full w-full object-cover')"
v-if="src && !error"
:src="src"
:alt="src || alt"
:onerror="() => (error = true)"
/>
<slot v-else-if="icon" name="icon">
<Icon :name="icon" :size="getIconSize" />
</slot>
<!-- Display initials if src was not passed -->
<span v-else>{{ createInitials }}</span>
<slot name="chip">
<span v-if="chip" :class="cn(chipClass)"></span>
</slot>
<slot :props="props" />
</div>
</template>
<script setup lang="ts">
import { cva, type VariantProps } from "class-variance-authority";
const variants = cva("font-medium ring-1 ring-muted", {
variants: {
size: {
xs: "h-4 w-4 text-xs",
sm: "h-6 w-6 text-xs",
md: "h-10 w-10 text-sm",
lg: "h-12 w-12 text-sm",
xl: "h-14 w-14",
"2xl": "h-16 w-16",
},
shape: {
circle: "rounded-full",
square: "rounded-none",
},
},
defaultVariants: {
size: "md",
shape: "circle",
},
});
type Props = VariantProps<typeof variants>;
const props = withDefaults(
defineProps<{
/**
* The size of the avatar
* @default md
*/
size?: Props["size"];
/**
* The shape of the avatar
* @default circle
*/
shape?: Props["shape"];
/**
* The image source
*/
src?: string;
/**
* The alt text for the image
*/
alt?: string;
/**
* The initials to display if no image is passed
*/
initials?: string;
/**
* The icon to display if no image is passed
*/
icon?: string;
/**
* The class for the chip
*/
chipClass?: string;
/**
* Whether to display the chip
* @default false
*/
chip?: boolean;
/**
* Custom class to apply
*/
class?: any;
}>(),
{
chipClass:
"absolute top-0 right-0 h-2.5 w-2.5 rounded-full bg-green-500 ring-1 ring-background",
}
);
defineSlots<{
default(props: any): void;
icon(): any;
chip(): any;
}>();
// Create a ref for the error state
const error = ref(false);
// Create initials from the alt/initials prop
const createInitials = computed(() => {
return useUpperCase(
(props.initials || props.alt || "")
.split(" ")
.map((word) => word.charAt(0))
.join("")
.substring(0, 2)
);
});
// Get the size of the icon based on the size prop
const getIconSize = computed(() => {
switch (props.size) {
case "xs":
return "14";
case "sm":
return "16";
case "md":
return "20";
case "lg":
return "24";
case "xl":
return "28";
case "2xl":
return "32";
}
});
</script>
Usage
Sizes
![https://randomuser.me/api/portraits/men/15.jpg](https://randomuser.me/api/portraits/men/15.jpg)
![https://randomuser.me/api/portraits/men/15.jpg](https://randomuser.me/api/portraits/men/15.jpg)
![https://randomuser.me/api/portraits/men/15.jpg](https://randomuser.me/api/portraits/men/15.jpg)
![https://randomuser.me/api/portraits/men/15.jpg](https://randomuser.me/api/portraits/men/15.jpg)
![https://randomuser.me/api/portraits/men/15.jpg](https://randomuser.me/api/portraits/men/15.jpg)
![https://randomuser.me/api/portraits/men/15.jpg](https://randomuser.me/api/portraits/men/15.jpg)
Table of contents