Client component
File Upload
File picking and drag-and-drop built around a hidden native
<input type="file">: the picker dialog, accept filtering and mobile
integrations stay the platform's. maxSize/maxFiles violations are
rejected and reported via onReject, never silently truncated.
Drag files here or click to browse
"use client";
import { FileUpload, Box, Text } from "astralis-ui";
import { useState } from "react";
export function FileUploadDemo() {
const [rejected, setRejected] = useState<string | null>(null);
return (
<Box w="full" maxW="md">
<FileUpload
multiple
maxFiles={3}
maxSize={5 * 1024 * 1024}
onReject={(rejections) =>
setRejected(rejections.map((r) => `${r.file.name} (${r.reason})`).join(", "))
}
>
<FileUpload.Dropzone />
<FileUpload.ItemGroup />
</FileUpload>
{rejected && (
<Text size="xs" color="muted" mt="2">
Rejected: {rejected}. Max 3 files, 5 MB each.
</Text>
)}
</Box>
);
}Drag files here or click to browse
import { FileUpload } from "astralis-ui";
<FileUpload>
<FileUpload.Dropzone />
<FileUpload.ItemGroup />
</FileUpload>
Import#
import { FileUpload } from "astralis-ui";Button trigger#
Skip the dropzone entirely: FileUpload.Trigger wraps any element and opens
the picker:
import { FileUpload, Button, HStack } from "astralis-ui";
import { Paperclip } from "lucide-react";
export function FileUploadTrigger() {
return (
<FileUpload multiple>
<HStack gap="3" alignItems="start">
<FileUpload.Trigger>
<Button variant="outline" colorScheme="gray" size="sm" leftIcon={<Paperclip size={14} />}>
Attach files
</Button>
</FileUpload.Trigger>
</HStack>
<FileUpload.ItemGroup />
</FileUpload>
);
}Props#
| Prop | Type | Default | Description |
|---|---|---|---|
accept | string | None | Native accept filter, such as "image/*,.pdf". The picker enforces it. |
multiple | boolean | false | Allow more than one file. |
maxFiles | number | None | Cap on the number of kept files when multiple is set. Picks past it are rejected. |
maxSize | number | None | Per-file size cap in bytes. Larger picks are rejected. |
onFilesChange | (files: File[]) => void | None | The full kept list after every add or remove. |
onReject | (rejections: { file, reason: "size" | "count" }[]) => void | None | The picks that were refused, and why. |
disabled | boolean | false | Locks picking and dropping. |
Parts#
| Prop | Type | Default | Description |
|---|---|---|---|
FileUpload.Dropzone | div props | None | Drop target that also opens the picker on click, Enter or Space. |
FileUpload.Trigger | children: ReactElement | None | Wraps your own element, such as a Button, to open the picker. |
FileUpload.ItemGroup | ul props | None | The kept files, each with its name, size and a remove button. |
Keyboard#
| Key | Action |
|---|---|
Tab | Move to the dropzone, then to each file's remove button |
Enter / Space on the dropzone | Open the file picker |
Enter / Space on a remove button | Remove that file |
Dragging files is pointer-only; the picker is the keyboard path to the same result.