Build websites 10x faster with HextaUI Blocks — Learn more
UI/UI

Avatar

An image element with a fallback for representing the user.

PSVCJD
<div className="flex gap-4 flex-wrap items-center">
  <Avatar>
    <AvatarImage
      src="https://github.com/preetsuthar17.png"
      alt="@preetsuthar17"
    />
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarImage src="https://github.com/fuma-nama.png" alt="@fuma-nama" />
    <AvatarFallback>VC</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarFallback>JD</AvatarFallback>
  </Avatar>
</div>

Installation

Install following dependencies:

npm install pnpm add @radix-ui/react-avatar class-variance-authority
pnpm add pnpm add @radix-ui/react-avatar class-variance-authority
yarn add pnpm add @radix-ui/react-avatar class-variance-authority
bun add pnpm add @radix-ui/react-avatar class-variance-authority

Required Component

This component requires the Tooltip component when using the tooltip functionality. Make sure you have installed and set up the Tooltip component before using tooltips with avatars.

Tooltip Component

Copy and paste the following code into your project.

components/ui/avatar.tsx
"use client";

import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "./Tooltip";

const avatarVariants = cva(
  "relative flex shrink-0 overflow-hidden rounded-full",
  {
    variants: {
      size: {
        xs: "h-6 w-6",
        sm: "h-8 w-8",
        md: "h-10 w-10",
        lg: "h-12 w-12",
        xl: "h-16 w-16",
        "2xl": "h-20 w-20",
      },
    },
    defaultVariants: {
      size: "md",
    },
  },
);

interface AvatarProps
  extends React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,
    VariantProps<typeof avatarVariants> {
  tooltip?: string | React.ComponentProps<typeof TooltipContent>;
}

const Avatar = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Root>,
  AvatarProps
>(({ className, size, tooltip, ...props }, ref) => {
  const avatar = (
    <AvatarPrimitive.Root
      ref={ref}
      className={cn(avatarVariants({ size }), className)}
      {...props}
    />
  );

  if (!tooltip) {
    return avatar;
  }

  const tooltipProps =
    typeof tooltip === "string" ? { children: tooltip } : tooltip;

  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger asChild>{avatar}</TooltipTrigger>
        <TooltipContent {...tooltipProps} />
      </Tooltip>
    </TooltipProvider>
  );
});
Avatar.displayName = AvatarPrimitive.Root.displayName;

const AvatarImage = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Image>,
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Image
    ref={ref}
    className={cn("aspect-square h-full w-full object-cover", className)}
    {...props}
  />
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;

const AvatarFallback = React.forwardRef<
  React.ElementRef<typeof AvatarPrimitive.Fallback>,
  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
  <AvatarPrimitive.Fallback
    ref={ref}
    className={cn(
      "flex h-full w-full items-center justify-center rounded-full bg-[hsl(var(--hu-muted))] text-[hsl(var(--hu-muted-foreground))] font-medium",
      className,
    )}
    {...props}
  />
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;

interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {
  max?: number;
  spacing?: "tight" | "normal" | "loose";
  size?: VariantProps<typeof avatarVariants>["size"];
  children: React.ReactElement[];
}

const avatarGroupSpacing = {
  tight: "-space-x-2",
  normal: "-space-x-1",
  loose: "space-x-1",
};

const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
  (
    { className, max = 3, spacing = "normal", size = "md", children, ...props },
    ref,
  ) => {
    const avatarsToShow = children.slice(0, max);
    const remainingCount = Math.max(0, children.length - max);

    return (
      <div
        ref={ref}
        className={cn(
          "flex items-center",
          avatarGroupSpacing[spacing],
          className,
        )}
        {...props}
      >
        {avatarsToShow.map((child, index) => {
          if (React.isValidElement(child)) {
            return React.cloneElement(child, {
              key: index,
              size,
              className: cn(
                "border-2 border-[hsl(var(--hu-background))]",
                (child.props as any)?.className,
              ),
            } as any);
          }
          return child;
        })}
        {remainingCount > 0 && (
          <Avatar
            size={size}
            className="border-2 border-[hsl(var(--hu-background))]"
          >
            <AvatarFallback className="bg-[hsl(var(--hu-secondary))] text-[hsl(var(--hu-secondary-foreground))] font-semibold">
              +{remainingCount}
            </AvatarFallback>
          </Avatar>
        )}
      </div>
    );
  },
);
AvatarGroup.displayName = "AvatarGroup";

export { Avatar, AvatarImage, AvatarFallback, AvatarGroup, avatarVariants };
export type { AvatarGroupProps };
npx hextaui@latest add avatar
pnpm dlx hextaui@latest add avatar
yarn dlx hextaui@latest add avatar
bun x hextaui@latest add avatar

Usage

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/Avatar";
<Avatar>
  <AvatarImage
    src="https://github.com/preetsuthar17.png"
    alt="@preetsuthar17"
  />
  <AvatarFallback>PS</AvatarFallback>
</Avatar>

Examples

Default

PS
<Avatar>
  <AvatarImage
    src="https://github.com/preetsuthar17.png"
    alt="@preetsuthar17"
  />
  <AvatarFallback>PS</AvatarFallback>
</Avatar>

Sizes

PSPSPSPSPSPS
<div className="flex gap-4 flex-wrap items-center">
  <Avatar size="xs">
    <AvatarImage
      src="https://github.com/preetsuthar17.png"
      alt="@preetsuthar17"
    />
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
  <Avatar size="sm">
    <AvatarImage
      src="https://github.com/preetsuthar17.png"
      alt="@preetsuthar17"
    />
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
  <Avatar size="md">
    <AvatarImage
      src="https://github.com/preetsuthar17.png"
      alt="@preetsuthar17"
    />
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
  <Avatar size="lg">
    <AvatarImage
      src="https://github.com/preetsuthar17.png"
      alt="@preetsuthar17"
    />
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
  <Avatar size="xl">
    <AvatarImage
      src="https://github.com/preetsuthar17.png"
      alt="@preetsuthar17"
    />
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
  <Avatar size="2xl">
    <AvatarImage
      src="https://github.com/preetsuthar17.png"
      alt="@preetsuthar17"
    />
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
</div>

Fallback Only

PSJDVC
<div className="flex gap-4 flex-wrap items-center">
  <Avatar>
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarFallback>JD</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarFallback>VC</AvatarFallback>
  </Avatar>
</div>

Avatar Group

Default Group (max 3)

PSVCJD+2

Large Group (max 5)

PSVCJDABXY+2
<div className="flex flex-col gap-6">
  <div className="space-y-2">
    <h4 className="text-sm font-medium">Default Group (max 3)</h4>
    <AvatarGroup>
      <Avatar>
        <AvatarImage
          src="https://github.com/preetsuthar17.png"
          alt="@preetsuthar17"
        />
        <AvatarFallback>PS</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarImage src="https://github.com/fuma-nama.png" alt="@fuma-nama" />
        <AvatarFallback>VC</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>JD</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>AB</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>XY</AvatarFallback>
      </Avatar>
    </AvatarGroup>
  </div>

  <div className="space-y-2">
    <h4 className="text-sm font-medium">Large Group (max 5)</h4>
    <AvatarGroup max={5} size="lg">
      <Avatar>
        <AvatarImage
          src="https://github.com/preetsuthar17.png"
          alt="@preetsuthar17"
        />
        <AvatarFallback>PS</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarImage src="https://github.com/fuma-nama.png" alt="@fuma-nama" />
        <AvatarFallback>VC</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>JD</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>AB</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>XY</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>MN</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>QR</AvatarFallback>
      </Avatar>
    </AvatarGroup>
  </div>
</div>

Group Spacing

Tight Spacing

PSVCJD

Normal Spacing

PSVCJD

Loose Spacing

PSVCJD
<div className="flex flex-col gap-6">
  <div className="space-y-2">
    <h4 className="text-sm font-medium">Tight Spacing</h4>
    <AvatarGroup spacing="tight">
      <Avatar>
        <AvatarImage
          src="https://github.com/preetsuthar17.png"
          alt="@preetsuthar17"
        />
        <AvatarFallback>PS</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarImage src="https://github.com/fuma-nama.png" alt="@fuma-nama" />
        <AvatarFallback>VC</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>JD</AvatarFallback>
      </Avatar>
    </AvatarGroup>
  </div>

  <div className="space-y-2">
    <h4 className="text-sm font-medium">Normal Spacing</h4>
    <AvatarGroup spacing="normal">
      <Avatar>
        <AvatarImage
          src="https://github.com/preetsuthar17.png"
          alt="@preetsuthar17"
        />
        <AvatarFallback>PS</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarImage src="https://github.com/fuma-nama.png" alt="@fuma-nama" />
        <AvatarFallback>VC</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>JD</AvatarFallback>
      </Avatar>
    </AvatarGroup>
  </div>

  <div className="space-y-2">
    <h4 className="text-sm font-medium">Loose Spacing</h4>
    <AvatarGroup spacing="loose">
      <Avatar>
        <AvatarImage
          src="https://github.com/preetsuthar17.png"
          alt="@preetsuthar17"
        />
        <AvatarFallback>PS</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarImage src="https://github.com/fuma-nama.png" alt="@fuma-nama" />
        <AvatarFallback>VC</AvatarFallback>
      </Avatar>
      <Avatar>
        <AvatarFallback>JD</AvatarFallback>
      </Avatar>
    </AvatarGroup>
  </div>
</div>

Avatar with Tooltip

Note: The Tooltip component is required to use the tooltip functionality with Avatar.

PSVCJD+1
<div className="flex gap-4 flex-wrap items-center">
  <Avatar tooltip="preetsuthar17">
    <AvatarImage
      src="https://github.com/preetsuthar17.png"
      alt="@preetsuthar17"
    />
    <AvatarFallback>PS</AvatarFallback>
  </Avatar>
  <Avatar tooltip={{ children: "fuma-nama", variant: "dark" }}>
    <AvatarImage src="https://github.com/fuma-nama.png" alt="@fuma-nama" />
    <AvatarFallback>VC</AvatarFallback>
  </Avatar>
  <Avatar tooltip="John Doe" size="lg">
    <AvatarFallback>JD</AvatarFallback>
  </Avatar>
  <Avatar
    tooltip={{ children: "Admin User", variant: "destructive" }}
    size="sm"
  >
    <AvatarFallback>AU</AvatarFallback>
  </Avatar>
</div>

Props

Avatar

PropTypeDefault
className?
string
undefined
tooltip?
string | TooltipContentProps
undefined
size?
"xs" | "sm" | "md" | "lg" | "xl" | "2xl"
"md"

AvatarGroup

PropTypeDefault
className?
string
undefined
children?
React.ReactElement[]
required
size?
"xs" | "sm" | "md" | "lg" | "xl" | "2xl"
"md"
spacing?
"tight" | "normal" | "loose"
"normal"
max?
number
3
Edit on GitHub

Last updated on