Bỏ qua đến nội dung

Ảnh đại diện

Hình đại diện người dùng kèm tùy chọn hiển thị chữ viết tắt thay thế cho menu hồ sơ, chuỗi bình luận và danh sách nhóm — với hiệu ứng viền dày theo phong cách tân thô mộc.

import {
  Avatar,
  AvatarBadge,

Avatar hiển thị ảnh người dùng và thay bằng AvatarFallback — chữ viết tắt hoặc một icon — trong lúc ảnh đang tải hoặc khi ảnh lỗi, mà không làm xô lệch bố cục. Được xây dựng trên thành phần Base UI Avatar và tạo kiểu theo công thức neobrutalist: viền dày 2px và chữ viết tắt bằng phông tiêu đề đậm.

Nên dùng khi:

  • Menu tài khoản — avatar trên header mở ra các hành động của tài khoản; xem ví dụ Dropdown.
  • Luồng bình luận và dòng hoạt động — một khuôn mặt bên cạnh mỗi dòng tác giả, với chữ viết tắt đỡ cho những ảnh lỗi hoặc thiếu.
  • Danh sách nhóm và thành viên — xếp các thành viên chồng lên nhau bằng AvatarGroup và giới hạn phần dư bằng AvatarGroupCount.

Cài đặt

pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/avatar.json
npx shadcn@latest add https://neobrutalism.com/r/base/avatar.json
yarn dlx shadcn@latest add https://neobrutalism.com/r/base/avatar.json
bunx --bun shadcn@latest add https://neobrutalism.com/r/base/avatar.json

Cài đặt các dependency sau:

pnpm add @base-ui/react
npm install @base-ui/react
yarn add @base-ui/react
bun add @base-ui/react

Sao chép và dán đoạn mã sau vào dự án của bạn.

components/ui/avatar.tsx
"use client"

import * as React from "react"
import { Avatar as AvatarPrimitive } from "radix-ui"

import { cn } from "@/lib/utils"

function Avatar({
  className,
  size = "default",
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
  size?: "default" | "sm" | "lg"
}) {
  return (
    <AvatarPrimitive.Root
      data-slot="avatar"
      data-size={size}
      className={cn(
        "group/avatar relative flex size-8 shrink-0 rounded-full border-2 select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
        className
      )}
      {...props}
    />
  )
}

function AvatarImage({
  className,
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
  return (
    <AvatarPrimitive.Image
      data-slot="avatar-image"
      className={cn(
        "aspect-square size-full rounded-full object-cover",
        className
      )}
      {...props}
    />
  )
}

function AvatarFallback({
  className,
  ...props
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
  return (
    <AvatarPrimitive.Fallback
      data-slot="avatar-fallback"
      className={cn(
        "flex size-full items-center justify-center rounded-full bg-muted font-head text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
        className
      )}
      {...props}
    />
  )
}

function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
  return (
    <span
      data-slot="avatar-badge"
      className={cn(
        "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
        "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
        "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
        "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
        className
      )}
      {...props}
    />
  )
}

function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="avatar-group"
      className={cn(
        "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
        className
      )}
      {...props}
    />
  )
}

function AvatarGroupCount({
  className,
  ...props
}: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="avatar-group-count"
      className={cn(
        "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
        className
      )}
      {...props}
    />
  )
}

export {
  Avatar,
  AvatarImage,
  AvatarFallback,
  AvatarGroup,
  AvatarGroupCount,
  AvatarBadge,
}

Cập nhật các đường dẫn import cho khớp với cấu trúc dự án của bạn.

Cách dùng

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
<Avatar>
  <AvatarImage src="https://github.com/shadcn.png" />
  <AvatarFallback>CN</AvatarFallback>
</Avatar>

Kết hợp

Dùng cách kết hợp sau để dựng một Avatar:

Avatar
├── AvatarImage
├── AvatarFallback
└── AvatarBadge

Dùng cách kết hợp sau để dựng một AvatarGroup:

AvatarGroup
├── Avatar
│   ├── AvatarImage
│   ├── AvatarFallback
│   └── AvatarBadge
├── Avatar
│   ├── AvatarImage
│   ├── AvatarFallback
│   └── AvatarBadge
└── AvatarGroupCount

Ví dụ

Cơ bản

Một thành phần avatar cơ bản với một hình ảnh và một bản dự phòng.

NB
import {
  Avatar,
  AvatarFallback,

Badge

Dùng thành phần AvatarBadge để thêm một badge vào avatar. Badge được đặt ở góc dưới bên phải của avatar.

import {
  Avatar,
  AvatarBadge,

Dùng prop className để thêm các kiểu tùy chỉnh cho badge như màu sắc, kích thước, v.v.

<Avatar>
  <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
  <AvatarFallback>CN</AvatarFallback>
  <AvatarBadge className="bg-green-600 dark:bg-green-800" />
</Avatar>

Badge với biểu tượng

Bạn cũng có thể dùng một biểu tượng bên trong <AvatarBadge>.

import { PlusIcon } from "lucide-react"

import {

Nhóm Avatar

Dùng thành phần AvatarGroup để thêm một nhóm avatar.

import {
  Avatar,
  AvatarFallback,

Bộ đếm nhóm Avatar

Dùng <AvatarGroupCount> để thêm một bộ đếm vào nhóm.

import {
  Avatar,
  AvatarFallback,

Nhóm Avatar với biểu tượng

Bạn cũng có thể dùng một biểu tượng bên trong <AvatarGroupCount>.

import { PlusIcon } from "lucide-react"

import {

Kích thước

Dùng prop size để thay đổi kích thước của avatar.

import {
  Avatar,
  AvatarFallback,

Bạn có thể dùng thành phần Avatar làm trigger cho một menu dropdown.

"use client"

import {

RTL

Để bật hỗ trợ RTL trong Neobrutalism, xem hướng dẫn cấu hình RTL.

"use client"

import * as React from "react"

Khả năng truy cập

Avatar là một hình ảnh, nên mọi quy tắc dành cho hình ảnh đều áp dụng: hãy cho AvatarImage một alt nêu tên người đó khi avatar là thứ duy nhất giúp nhận ra họ, và alt="" khi tên đã hiện ngay bên cạnh — nếu không trình đọc màn hình sẽ đọc tên hai lần. Chữ viết tắt trong AvatarFallback là văn bản thường và sẽ được đọc lên, nên hãy giữ cho chúng có nghĩa. Khi avatar mở một menu (xem Dropdown), hãy đặt tên hỗ trợ truy cập lên nút trigger chứ không phải lên ảnh.

Tham chiếu API

Avatar

Thành phần Avatar là thành phần gốc bao bọc hình ảnh avatar và bản dự phòng.

PropTypeDefault
size"default" | "sm" | "lg""default"
classNamestring-

AvatarImage

Thành phần AvatarImage hiển thị hình ảnh avatar. Nó nhận tất cả prop của Base UI Avatar Image.

PropTypeDefault
srcstring-
altstring-
classNamestring-

AvatarFallback

Thành phần AvatarFallback hiển thị một bản dự phòng khi hình ảnh không tải được. Nó nhận tất cả prop của Base UI Avatar Fallback.

PropTypeDefault
classNamestring-

AvatarBadge

Thành phần AvatarBadge hiển thị một chỉ báo badge trên avatar, thường được đặt ở góc dưới bên phải.

PropTypeDefault
classNamestring-

AvatarGroup

Thành phần AvatarGroup hiển thị một nhóm avatar với kiểu chồng lên nhau.

PropTypeDefault
classNamestring-

AvatarGroupCount

Thành phần AvatarGroupCount hiển thị một chỉ báo bộ đếm trong nhóm avatar, thường cho biết số lượng avatar bổ sung.

PropTypeDefault
classNamestring-

Để biết thêm thông tin về prop của Base UI Avatar, xem tài liệu Base UI.