File Upload Attachment Upload
beui/attachment-uploadFreeBlock
A mixed attachment workspace with a dropzone, staggered file and image rows, animated upload, success, failure, retry and removal feedback, shared-layout image previews, and an audio waveform.
Install it
npx shadcn@latest add https://beui.dev/r/attachment-upload.jsonSource
1"use client";2// beui.dev/components/blocks/file-upload34import {5 AlertCircle,6 Check,7 ExternalLink,8 FileImage,9 Link as LinkIcon,10 LoaderCircle,11 Mic,12 Paperclip,13 Pause,14 Play,15 RotateCcw,16 Upload,17 X,18} from "lucide-react";19import {20 AnimatePresence,21 LayoutGroup,22 motion,23 useReducedMotion,24} from "motion/react";25import {26 useCallback,27 useEffect,28 useId,29 useRef,30 useState,31} from "react";32import { createPortal } from "react-dom";33import { Tooltip } from "@/components/motion/tooltip";34import {35 EASE_OUT,36 SPRING_LAYOUT,37 SPRING_PRESS,38} from "@/lib/ease";39import { cn } from "@/lib/utils";4041export type AttachmentUploadKind = "file" | "link" | "image" | "audio";42export type AttachmentRejectReason = "too-large" | "max-files";43export type AttachmentUploadStatus =44 | "idle"45 | "uploading"46 | "complete"47 | "failed";4849export type AttachmentUploadItem = {50 id: string;51 name: string;52 kind: AttachmentUploadKind;53 size?: number;54 href?: string;55 previewUrl?: string;56 currentTime?: number;57 duration?: number;58 status?: AttachmentUploadStatus;59 error?: string;60 file?: File;61};6263export type AttachmentUploadClassNames = {64 dropzone?: string;65 list?: string;66 row?: string;67};6869export interface AttachmentUploadProps {70 value?: AttachmentUploadItem[];71 defaultValue?: AttachmentUploadItem[];72 onValueChange?: (items: AttachmentUploadItem[]) => void;73 onFilesAdded?: (items: AttachmentUploadItem[], files: File[]) => void;74 onFilesRejected?: (files: File[], reason: AttachmentRejectReason) => void;75 onRemove?: (item: AttachmentUploadItem) => void;76 onRetry?: (item: AttachmentUploadItem) => void;77 playingId?: string;78 onAudioToggle?: (item: AttachmentUploadItem) => void;79 accept?: string;80 multiple?: boolean;81 maxFiles?: number;82 maxFileSize?: number;83 disabled?: boolean;84 title?: string;85 description?: string;86 attachmentsLabel?: string;87 className?: string;88 classNames?: AttachmentUploadClassNames;89}9091const ITEM_TRANSITION = { duration: 0.2, ease: EASE_OUT } as const;92const DEFAULT_MAX_FILE_SIZE = 500 * 1024 * 1024;93const UPLOAD_PROGRESS_MS = 900;94const UPLOAD_COMPLETE_HOLD_MS = 1000;95const REMOVE_PENDING_MS = 420;9697const WAVEFORM_BARS = [98 18, 31, 24, 39, 30, 43, 27, 18, 9, 29, 38, 24, 34, 18, 26, 37, 21, 14,99 7, 11, 22, 35, 18, 26, 41, 29, 17, 33,100].map((height, index) => ({ id: `wave-${index}-${height}`, height }));101102function useControllableList<T>({103 value,104 defaultValue,105 onValueChange,106}: {107// … truncated
What it pulls in
npm packages
Files it writes
More from beui
All 104 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Availability Scheduleravailability-scheduler | beui | Blocks | Free | 4 deps · 15 files | |
| Bloom Menubloom-menu | beui | Blocks | Free | 4 deps · 3 files | |
| Command Palettecommand-palette | beui | Blocks | Free | 4 deps · 3 files | |
| Dynamic Islanddynamic-island | beui | Blocks | Free | 3 deps · 3 files | |
| Expandable Action Barexpandable-action-bar | beui | Blocks | Free | 3 deps · 2 files | |
| Expandable Tabsexpandable-tabs | beui | Blocks | Free | 3 deps · 3 files | |
| Feedback Widgetfeedback-widget | beui | Blocks | Free | 4 deps · 9 files | |
| File Upload Upload Queuefile-upload | beui | Blocks | Free | 4 deps · 3 files |
