logo

Aki UI

アキ ウイ

File Upload

ファイルアップロード

A single-file drop target that carries its own state: validation, progress and the error message. The whole of the drag and drop API is wired by hand, which means dealing with the one thing it gets wrong — dragleave also fires when the pointer crosses onto a child element, so the target flickers back to idle while the file is still over it. A depth counter incremented on enter and decremented on leave decides when the pointer has genuinely left. Clicking is a real file input, only visually hidden, with the button as its label — so it opens from the keyboard and takes a focus ring. Where Dropzone wraps react-dropzone and hands the state back to you as slots, this is one self-contained file that owns the state itself. The progress bar is a stand-in: swap the interval for the events of whatever actually moves the bytes. Adapted from Kokonut UI under the MIT licence.

Drag and drop a file

PNG, JPEG, PDF up to 2 MB

Installation

Install the following dependencies:1

npm install framer-motionnpm install lucide-react

Copy and paste the following code into a new component file:2

"use client"

/**
* Adapted for Aki UI from Kokonut UI's File Upload.
* Original author: @dorianbaffier — https://kokonutui.com
* Copyright (c) 2025 kokonutUI, MIT License.
* https://github.com/kokonut-labs/kokonutui
*
* Reworked here onto the local palette, with motion/react swapped for
* framer-motion, the drop target rebuilt around a real file input and a

How to use it3

"use client"

import { FileUpload } from "@/components/ui/file-upload"

export default function Page() {
return (
<FileUpload
accept={["image/png", "image/jpeg"]}
maxSize={2 * 1024 * 1024}
onComplete={(file) => console.log(file.name)}

Props

FileUpload

NameDefaultDescription
accept[]

string[]

Accepted MIME types, wildcards allowed; empty accepts anything

maxSize5242880

number

Largest accepted file in bytes

uploadDelay2000

number

Milliseconds the simulated transfer takes; 0 completes at once

validate

(file: File) => string | null

Extra check; return a message to reject the file

onSelect

(file: File) => void

Called once a file has passed validation

onComplete

(file: File) => void

Called when the transfer reaches 100%

onError

(error: FileUploadError) => void

Called with the code and message of a rejection

onRemove

() => void

Called when the file is cancelled or removed

darkfalse

boolean

Render the dark palette