Avatar Uploader
A circular avatar uploader with hover overlay for profile pictures.
Preview
Profile Picture
JPG, GIF or PNG. Max size of 800K
Code
1import {2 UplofileRoot,3 UplofileTrigger,4 UplofilePreview,5 type UploadFileItem,6} from "@/components/ui/uplofile";7import { IoPersonOutline, IoCameraOutline, IoReloadOutline, IoAlertCircleOutline, IoRefreshOutline } from "react-icons/io5";8import { mockUpload, cn } from "@/lib/utils.ts";9import { UplofileRetry } from "@/components/ui/uplofile";1011export default function AvatarUploaderDemo() {12 return (13 <div className="flex flex-col items-center justify-center gap-4">14 <UplofileRoot upload={mockUpload} accept="image/*" multiple={false}>15 <UplofilePreview16 render={({ items }) => <AvatarPreview items={items} />}17 />18 </UplofileRoot>19 <div className="text-center">20 <p className="text-sm font-medium">Profile Picture</p>21 <p className="text-xs text-muted-foreground text-balance">22 JPG, GIF or PNG. Max size of 800K23 </p>24 </div>25 </div>26 );27}2829function AvatarPreview({ items }: { items: UploadFileItem[] }) {30 const item = items[0];3132 return (33 <div className="relative group">34 <div className="relative">35 <UplofileTrigger>36 <div37 className={cn(38 "relative w-32 h-32 rounded-full overflow-hidden cursor-pointer border-4 border-background shadow-xl bg-muted flex items-center justify-center transition-all hover:scale-[1.02] active:scale-[0.98]",39 item?.status === "error" &&40 "border-destructive/50 ring-4 ring-destructive/10",41 )}42 >43 {item?.previewUrl ? (44 <img45 src={item.previewUrl}46 alt="Avatar"47 className={cn(48 "w-full h-full object-cover animate-in fade-in duration-500",49 item.status === "error" && "opacity-40 grayscale",50 )}51 />52 ) : (53 <IoPersonOutline className="w-16 h-16 text-muted-foreground/50" />54 )}5556 <div className="absolute inset-0 bg-black/40 flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-200">57 <IoCameraOutline className="w-8 h-8 text-white mb-1" />58 <span className="text-white text-[10px] font-medium uppercase tracking-wider">59 Change Photo60 </span>61 </div>6263 {item?.status === "uploading" && (64 <div className="absolute inset-0 bg-black/60 flex flex-col items-center justify-center backdrop-blur-[2px]">65 <IoReloadOutline className="w-8 h-8 text-white animate-spin mb-2" />66 <span className="text-white text-xs font-bold">67 {item.progress}%68 </span>69 </div>70 )}7172 {item?.status === "error" && (73 <div className="absolute inset-0 bg-destructive/60 flex flex-col items-center justify-center backdrop-blur-[1px]">74 <IoAlertCircleOutline className="w-8 h-8 text-white mb-1" />75 <span className="text-white text-[10px] font-bold uppercase">76 Error77 </span>78 </div>79 )}80 </div>81 </UplofileTrigger>8283 {item?.status === "done" && (84 <div className="absolute -bottom-1 -right-1 w-8 h-8 bg-emerald-500 rounded-full border-4 border-background flex items-center justify-center shadow-lg animate-in zoom-in">85 <div className="w-2 h-2 bg-white rounded-full" />86 </div>87 )}8889 {item?.status === "error" && (90 <UplofileRetry uid={item.uid} asChild>91 <button className="absolute -bottom-1 -right-1 w-8 h-8 bg-destructive text-white rounded-full border-4 border-background flex items-center justify-center shadow-lg animate-in zoom-in hover:scale-110 transition-transform">92 <IoRefreshOutline className="w-4 h-4" />93 </button>94 </UplofileRetry>95 )}96 </div>97 </div>98 );99}
Key Points
- → Single file upload (no
multipleprop) - → Hover overlay for change action
- → Circular crop with
rounded-full - → Uses
item.previewUrlfor instant preview