<script setup>
import { FileUpload } from "@dicehub/kappa/components/file-upload";
</script>
<template>
<FileUpload.Root :max-files="4" name="project-files">
<FileUpload.Label>Project files</FileUpload.Label>
<FileUpload.Dropzone disable-click>
<div>
<strong>Drop project files here</strong>
<span>Mesh, configuration, or result files · up to 250 MB</span>
</div>
<FileUpload.Trigger>Choose files</FileUpload.Trigger>
</FileUpload.Dropzone>
<FileUpload.Context v-slot="{ acceptedFiles }">
<FileUpload.ItemGroup>
<FileUpload.Item v-for="file in acceptedFiles" :key="file.name" :file="file">
<FileUpload.ItemPreview />
<FileUpload.ItemName />
<FileUpload.ItemSizeText />
<FileUpload.ItemDeleteTrigger :aria-label="`Remove ${file.name}`" />
</FileUpload.Item>
</FileUpload.ItemGroup>
</FileUpload.Context>
<FileUpload.HiddenInput />
</FileUpload.Root>
</template>Installation
File Upload is part of the main Kappa package. Kappa wraps the Ark UI Vue file-upload primitive and adds semantic-token styling, compact file rows, and useful default media and actions.
Barrel
import {
FileUpload,
type FileUploadProps,
type FileUploadFileRejectDetails,
} from "@dicehub/kappa";Granular
import {
FileUpload,
type FileUploadProps,
type FileUploadFileRejectDetails,
} from "@dicehub/kappa/components/file-upload";Usage
Add disableClick to a drop zone that contains a Trigger. This avoids opening the native picker twice. Render file rows from acceptedFiles inFileUpload.Context.
<script setup>
import { FileUpload } from "@dicehub/kappa/components/file-upload";
</script>
<template>
<FileUpload.Root :max-files="3" name="geometry">
<FileUpload.Label>Geometry files</FileUpload.Label>
<FileUpload.Dropzone disable-click>
<span>Drag files here or select them from your device</span>
<FileUpload.Trigger>Browse files</FileUpload.Trigger>
</FileUpload.Dropzone>
<FileUpload.Context v-slot="{ acceptedFiles }">
<FileUpload.ItemGroup>
<FileUpload.Item v-for="file in acceptedFiles" :key="file.name" :file="file">
<FileUpload.ItemPreview />
<FileUpload.ItemName />
<FileUpload.ItemSizeText />
<FileUpload.ItemDeleteTrigger :aria-label="`Remove ${file.name}`" />
</FileUpload.Item>
</FileUpload.ItemGroup>
</FileUpload.Context>
<FileUpload.HiddenInput />
</FileUpload.Root>
</template>Composition
FileUpload.Root
├── FileUpload.Label
├── FileUpload.Dropzone
│ └── FileUpload.Trigger
├── FileUpload.Context
│ └── FileUpload.ItemGroup [accepted | rejected]
│ └── FileUpload.Item [file]
│ ├── FileUpload.ItemPreview
│ │ └── FileUpload.ItemPreviewImage (optional)
│ ├── FileUpload.ItemName
│ ├── FileUpload.ItemSizeText
│ └── FileUpload.ItemDeleteTrigger
├── FileUpload.ClearTrigger (optional)
└── FileUpload.HiddenInputArk UI owns file selection, drag state, validation, native input behavior, and accepted or rejected collections. Kappa does not send files. The application owns the transfer, cancellation, retry, and progress state.
Rootdefines constraints and file state.RootProvideraccepts a machine fromuseFileUpload.Dropzoneaccepts drops.Triggeropens the operating system file picker.ItemGroupandItemrender accepted or rejected files.- Compose Kappa
Progressinside an item when the application starts a transfer.
<script setup>
import { FileUpload, useFileUpload } from "@dicehub/kappa/components/file-upload";
const upload = useFileUpload({ maxFiles: 5 });
</script>
<template>
<FileUpload.RootProvider :value="upload">
<FileUpload.Dropzone>Drop files here</FileUpload.Dropzone>
<FileUpload.HiddenInput />
</FileUpload.RootProvider>
</template>Examples
Upload Dialog with Progress
Open a focused upload flow, explain the limits, select or drop files, and report transfer progress.
<script setup lang="ts">
import { ref } from "vue";
import { Button, Dialog, FileUpload, Progress } from "@dicehub/kappa";
const files = ref<File[]>([]);
const uploading = ref(false);
const progress = ref<Record<string, number>>({});
const upload = async () => {
uploading.value = true;
// Update progress[file.name] from the application's upload client.
await Promise.all(files.value.map((file) => uploadFile(file, progress)));
uploading.value = false;
};
</script>
<template>
<Dialog.Root :close-on-escape="!uploading" :disable-pointer-dismissal="uploading">
<Dialog.Trigger as-child><Button>Upload files</Button></Dialog.Trigger>
<Dialog.Content :show-close-button="!uploading" size="lg">
<Dialog.Header>
<Dialog.Title>Upload project files</Dialog.Title>
<Dialog.Description>Add geometry, configuration, or result files.</Dialog.Description>
</Dialog.Header>
<FileUpload.Root v-model:accepted-files="files" :disabled="uploading" :max-files="5">
<FileUpload.Dropzone disable-click>
<span>Drop files anywhere in this area</span>
<FileUpload.Trigger>Choose files</FileUpload.Trigger>
</FileUpload.Dropzone>
<FileUpload.Context v-slot="{ acceptedFiles }">
<FileUpload.ItemGroup>
<FileUpload.Item v-for="file in acceptedFiles" :key="file.name" :file="file">
<FileUpload.ItemPreview />
<FileUpload.ItemName />
<FileUpload.ItemSizeText />
<Progress.Root v-if="progress[file.name] != null" :model-value="progress[file.name]">
<Progress.Track><Progress.Range /></Progress.Track>
</Progress.Root>
</FileUpload.Item>
</FileUpload.ItemGroup>
</FileUpload.Context>
<FileUpload.HiddenInput />
</FileUpload.Root>
<Dialog.Footer><Button :loading="uploading" @click="upload">Upload</Button></Dialog.Footer>
</Dialog.Content>
</Dialog.Root>
</template>File Constraints
Restrict file types, sizes, and counts. Keep rejected files visible with a clear recovery message.
STL or STEP files, up to 2 MB each. Maximum two files.
<FileUpload.Root
accept=".stl,.step"
:max-file-size="2 * 1024 * 1024"
:max-files="2"
@file-reject="showFileErrors"
>
<FileUpload.Label>Surface geometry</FileUpload.Label>
<FileUpload.Dropzone disable-click>
<span>Drop STL or STEP files</span>
<FileUpload.Trigger>Select geometry</FileUpload.Trigger>
</FileUpload.Dropzone>
<!-- Render acceptedFiles and rejectedFiles from FileUpload.Context. -->
<FileUpload.HiddenInput />
</FileUpload.Root>Disabled and Invalid
Set state at the root so the label, drop zone, trigger, and input stay synchronized.
<FileUpload.Root disabled>
<FileUpload.Label>Disabled upload</FileUpload.Label>
<FileUpload.Dropzone>Upload unavailable</FileUpload.Dropzone>
<FileUpload.HiddenInput />
</FileUpload.Root>
<FileUpload.Root invalid>
<FileUpload.Label>Invalid upload</FileUpload.Label>
<FileUpload.Dropzone>Select a supported project file</FileUpload.Dropzone>
<FileUpload.HiddenInput />
</FileUpload.Root>Accessibility
- Use a visible
Labelfor every upload field. - The drop zone and trigger support keyboard file selection. Do not replace them with pointer-only handlers.
- Give each delete action a file-specific accessible name.
- Report rejection and transfer status in an
aria-liveregion. - Do not close a modal while a transfer is active unless cancellation is safe and explicit.
- Drag, focus, invalid, disabled, and rejected states stay visible in both themes and forced colors.
See the official Ark UI File Upload documentationfor the underlying behavior and accessibility contract.
API Reference
FileUpload.Root
| Prop | Type | Default | Description |
|---|---|---|---|
acceptedFiles / v-model:acceptedFiles | File[] | — | Controlled accepted files. |
defaultAcceptedFiles | File[] | [] | Initial files for uncontrolled use. |
accept | string | string[] | record | — | Allowed MIME types or file extensions. |
maxFiles | number | 1 | Maximum accepted file count. |
minFileSize / maxFileSize | number | 0 / Infinity | Accepted byte range for each file. |
allowDrop | boolean | true | Enables drag-and-drop selection. |
preventDocumentDrop | boolean | true | Prevents an outside drop from navigating the document. |
directory | boolean | false | Allows directory selection in supported browsers. |
capture | "user" | "environment" | — | Selects a device camera for media capture. |
disabled / readOnly / required / invalid | boolean | false | Availability, form, and validation states. |
name | string | — | Name for the native hidden file input. |
validate | (file, details) => FileError[] | null | — | Runs custom validation for each file. |
transformFiles | (files) => Promise<File[]> | — | Transforms files before Ark accepts them. |
Parts
| Part | Element | Description |
|---|---|---|
FileUpload.Root | div | Owns file selection, constraints, and accepted or rejected state. |
FileUpload.RootProvider | div | Uses an external useFileUpload machine. |
FileUpload.Label | label | Names the file input. |
FileUpload.Dropzone | div | Accepts pointer clicks and file drops. Use disableClick with a nested Trigger. |
FileUpload.Trigger | button | Opens the native file picker. |
FileUpload.ItemGroup | ul | Groups accepted or rejected files. |
FileUpload.Item | li | Provides one File to its item parts. |
FileUpload.ItemPreview / ItemPreviewImage | div / img | Shows generic or image-specific file media. |
FileUpload.ItemName / ItemSizeText | div | Shows the file name and localized size. |
FileUpload.ItemDeleteTrigger | button | Removes one file. A default close icon is included. |
FileUpload.ClearTrigger | button | Removes all accepted files. |
FileUpload.HiddenInput | input | Provides native form and file-picker behavior. |
FileUpload.Context | slot | Exposes accepted files, rejected files, limits, and machine actions. |
Events
| Event | Payload | Description |
|---|---|---|
update:acceptedFiles | File[] | Emitted when controlled accepted files change. |
fileAccept | { files: File[] } | Emitted when files pass all constraints. |
fileReject | { files: FileRejection[] } | Emitted when files fail a constraint or custom validation. |
fileChange | { acceptedFiles, rejectedFiles } | Emitted after any file selection change. |
Data Attributes
| Attribute | Value | Description |
|---|---|---|
data-slot | "file-upload" / "file-upload-dropzone" / … | Stable Kappa selectors for each named part. |
data-dragging | "" | Present while files are over the drop zone. |
data-invalid / data-disabled / data-readonly | "" | Validation and availability state from Ark UI. |
data-type | "accepted" | "rejected" | File-list and item category. |
Exports
| Export | Description |
|---|---|
FileUpload | Compound namespace and root component. |
FileUpload.Root / RootProvider | Machine-owned and provider-owned roots. |
FileUpload.Label / Dropzone / Trigger / HiddenInput | File-selection parts. |
FileUpload.ItemGroup / Item / Item* / ClearTrigger | Accepted and rejected file-list parts. |
FileUpload.Context | Scoped access to current machine state and actions. |
FileUploadProps / FileUploadEmits | Kappa root props and Vue event contracts. |
FileUploadApi / FileUploadContextValue | Provider and context API types. |
useFileUpload / useFileUploadContext / fileUploadAnatomy | Ark UI hook and anatomy re-exports. |