# EdgeStore Docs: Avatar URL: https://edgestore.dev/docs/components/avatar Source: https://raw.githubusercontent.com/edgestorejs/edgestore/refs/heads/main/docs/content/docs/components/avatar.mdx import { DemoBlock } from '@/components/demo-block'; import { LimitedCode } from '@/components/ui/limited-code'; import { OpenTabs, OpenTabsContent, OpenTabsList, OpenTabsTrigger, } from '@/components/ui/open-tabs'; import AvatarDropzoneBlock from '@/components/upload/blocks/avatar-block'; import { Step, Steps } from 'fumadocs-ui/components/steps'; Click or drop onto the image to replace it. With `minDimension`, the image's pixel size is checked in the browser before it is uploaded. ## Installation CLI Manual Use the shadcn CLI to add the component to your project. npm pnpm yarn bun ```bash npx shadcn@latest add https://edgestore.dev/r/avatar-dropzone.json ``` ```bash pnpm dlx shadcn@latest add https://edgestore.dev/r/avatar-dropzone.json ``` ```bash yarn dlx shadcn@latest add https://edgestore.dev/r/avatar-dropzone.json ``` ```bash bun x shadcn@latest add https://edgestore.dev/r/avatar-dropzone.json ``` ### Setup for manual installation First you will need to follow the [manual install setup](./manual-install) guide. ### Install required components * [uploader-provider](./uploader-provider) * [dropzone](./dropzone) * [progress-circle](./progress-circle) * [shadcn/ui button](https://ui.shadcn.com/docs/components/button) ### Copy this component ````tsx title="components/upload/avatar-dropzone.tsx" 'use client'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { CameraIcon, Loader2Icon, UserIcon } from 'lucide-react'; import * as React from 'react'; import { type Accept } from 'react-dropzone'; import { dropzoneState, useUploadDropzone } from './dropzone'; import { ProgressCircle } from './progress-circle'; import { formatFileSize, IMAGE_ACCEPT, minImageSize, useObjectUrl, } from './upload-utils'; import { useUploader } from './uploader-provider'; /** * Props for the AvatarDropzone component. */ export type AvatarDropzoneProps = Omit< React.ComponentProps<'div'>, 'children' > & { /** * Label shown next to the image and used in accessible labels. * @default "Profile photo" */ label?: string; /** * Help text shown under the label. Defaults to a summary of the limits. */ description?: React.ReactNode; /** * Shape of the image. * @default "circle" */ shape?: 'circle' | 'square'; /** * Size of the image in pixels. * @default 88 */ size?: number; /** * Maximum file size in bytes. */ maxSize?: number; /** * Minimum width and height of the image in pixels. */ minDimension?: number; /** * Accepted image types. * @default PNG, JPG, WEBP and GIF */ accept?: Accept; /** * Human-readable list of accepted types, shown in the description and messages. * @default "PNG, JPG, WEBP or GIF" */ typesLabel?: string; /** * Shown when there is no image. */ placeholder?: React.ReactNode; /** * Whether the dropzone is disabled. */ disabled?: boolean; }; /** * A single image in a fixed shape, for profile photos and logos. * Click or drop onto the image to replace it. * * @example * ```tsx * * ``` */ export function AvatarDropzone({ label = 'Profile photo', description, shape = 'circle', size = 88, maxSize, minDimension, accept = IMAGE_ACCEPT, typesLabel = 'PNG, JPG, WEBP or GIF', placeholder, disabled, className, ...props }: AvatarDropzoneProps) { const { fileStates, removeFile, uploadFiles } = useUploader(); const fileState = fileStates[0]; const preview = useObjectUrl(fileState?.file); const src = preview ?? fileState?.url; const validator = React.useMemo( () => (minDimension ? minImageSize(minDimension) : undefined), [minDimension], ); const { getRootProps, getInputProps, open, isDragActive, isDragReject, isProcessing, errors, clearErrors, } = useUploadDropzone({ accept, maxSize, typesLabel, disabled, validator, replace: true, }); const isBusy = isProcessing || fileState?.status === 'UPLOADING'; const accessibleLabel = label.toLowerCase(); return (
{src ? ( ) : ( (placeholder ?? ( )) )} {!disabled && ( )} {isBusy && ( {isProcessing ? ( ) : ( = 72} className="text-[10px]" /> )} )}

{label}

{description ?? [ typesLabel, minDimension && `at least ${minDimension}×${minDimension}px`, maxSize && `up to ${formatFileSize(maxSize)}`, ] .filter(Boolean) .join(', ')}

{!disabled && (
{fileState && ( )}
)} {fileState?.status === 'ERROR' && (

{fileState.error ?? 'Upload failed'}{' '} {!disabled && ( )}

)} {errors[0] && (

{errors[0]}

)}
); } ````
## Usage Install or copy the component from [Installation](#installation) before using this example. ```tsx 'use client'; import { AvatarDropzone } from '@/components/upload/avatar-dropzone'; import { UploaderProvider, type UploadFn, } from '@/components/upload/uploader-provider'; import { useEdgeStore } from '@/lib/edgestore'; import * as React from 'react'; export function AvatarDropzoneUsage() { const { edgestore } = useEdgeStore(); const uploadFn: UploadFn = React.useCallback( async ({ file, onProgressChange, signal }) => { const res = await edgestore.publicImages.upload({ file, signal, onProgressChange, }); // you can run some server action or api here // to add the necessary data to your database console.log(res); return res; }, [edgestore], ); return ( ); } ``` ## Props | Prop | Type | Default | Description | | -------------- | ---------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `label` | `string` | `"Profile photo"` | Label shown next to the image and used in accessible labels. | | `description` | `ReactNode` | | Help text under the label. Defaults to a summary of the limits. | | `shape` | `"circle" \| "square"` | `"circle"` | Shape of the image. | | `size` | `number` | `88` | Size of the image in pixels. | | `minDimension` | `number` | | Minimum width and height of the image in pixels. | | `placeholder` | `ReactNode` | user icon | Shown when there is no image. | | `maxSize` | `number` | | Maximum file size in bytes. | | `accept` | `Accept` | PNG, JPG, WEBP, GIF | Accepted file types, in [react-dropzone's format](https://react-dropzone.js.org/#section-accepting-specific-file-types). | | `typesLabel` | `string` | `"PNG, JPG, WEBP or GIF"` | Human-readable list of accepted types, shown in the hint and in error messages. | | `disabled` | `boolean` | `false` | Disables the component. |