All components
Image Cover Dropzone
file-uploadOrigin UI component.
responsive · 600px
Install
Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:
$
npx shadcn@latest add https://your-domain/r/comp-544.jsonUsage
import Cmp from "@/registry/origin-ui/comp-544";
export default function Demo() {
return <Cmp />;
}Component source
"use client";
import { AlertCircleIcon, ImageUpIcon, XIcon } from "lucide-react";
import { useFileUpload } from "@/registry/origin-ui/use-file-upload";
export default function Component() {
const maxSizeMB = 5;
const maxSize = maxSizeMB * 1024 * 1024; // 5MB default
const [
{ files, isDragging, errors },
{
handleDragEnter,
handleDragLeave,
handleDragOver,
handleDrop,
openFileDialog,
removeFile,
getInputProps,
},
] = useFileUpload({
accept: "image/*",
maxSize,
});
const previewUrl = files[0]?.preview || null;
return (
<div className="flex flex-col gap-2">
<div className="relative">
{/* Drop area */}
<div
className="relative flex min-h-52 flex-col items-center justify-center overflow-hidden rounded-xl border border-input border-dashed p-4 transition-colors hover:bg-accent/50 has-disabled:pointer-events-none has-[input:focus]:border-ring has-[img]:border-none has-disabled:opacity-50 has-[input:focus]:ring-[3px] has-[input:focus]:ring-ring/50 data-[dragging=true]:bg-accent/50"
data-dragging={isDragging || undefined}
onClick={openFileDialog}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onDragOver={handleDragOver}
onDrop={handleDrop}
role="button"
tabIndex={-1}
>
<input
{...getInputProps()}
aria-label="Upload file"
className="sr-only"
/>
{previewUrl ? (
<div className="absolute inset-0">
<img
alt={files[0]?.file?.name || "Uploaded image"}
className="size-full object-cover"
src={previewUrl}
/>
</div>
) : (
<div className="flex flex-col items-center justify-center px-4 py-3 text-center">
<div
aria-hidden="true"
className="mb-2 flex size-11 shrink-0 items-center justify-center rounded-full border bg-background"
>
<ImageUpIcon className="size-4 opacity-60" />
</div>
<p className="mb-1.5 font-medium text-sm">
Drop your image here or click to browse
</p>
<p className="text-muted-foreground text-xs">
Max size: {maxSizeMB}MB
</p>
</div>
)}
</div>
{previewUrl && (
<div className="absolute top-4 right-4">
<button
aria-label="Remove image"
className="z-50 flex size-8 cursor-pointer items-center justify-center rounded-full bg-black/60 text-white outline-none transition-[color,box-shadow] hover:bg-black/80 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
onClick={() => removeFile(files[0]?.id)}
type="button"
>
<XIcon aria-hidden="true" className="size-4" />
</button>
</div>
)}
</div>
{errors.length > 0 && (
<div
className="flex items-center gap-1 text-destructive text-xs"
role="alert"
>
<AlertCircleIcon className="size-3 shrink-0" />
<span>{errors[0]}</span>
</div>
)}
<p
aria-live="polite"
className="mt-2 text-center text-muted-foreground text-xs"
role="region"
>
Single image uploader w/ max size ∙{" "}
<a
className="underline hover:text-foreground"
href="https://github.com/cosscom/coss/blob/main/apps/origin/docs/use-file-upload.md"
>
API
</a>
</p>
</div>
);
}Source: Origin UI