Skip to content
Kappa
File Upload
@dicehub/kappav0.4.2

File Upload

Selects and validates files with a keyboard-accessible picker and drag-and-drop surface.

Drop project files hereMesh, configuration, or result files · up to 250 MB
<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.

Drag files hereor select them from your device
<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

Typical part hierarchy
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.HiddenInput

Ark 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.

  • Root defines constraints and file state. RootProvider accepts a machine from useFileUpload.
  • Dropzone accepts drops. Trigger opens the operating system file picker.
  • ItemGroup and Item render accepted or rejected files.
  • Compose Kappa Progress inside 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

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.

Drop STL or STEP filesTwo files maximum

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.

Upload unavailable
Select a supported project file
<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 Label for 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-live region.
  • 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

PropTypeDefaultDescription
acceptedFiles / v-model:acceptedFilesFile[]—Controlled accepted files.
defaultAcceptedFilesFile[][]Initial files for uncontrolled use.
acceptstring | string[] | record—Allowed MIME types or file extensions.
maxFilesnumber1Maximum accepted file count.
minFileSize / maxFileSizenumber0 / InfinityAccepted byte range for each file.
allowDropbooleantrueEnables drag-and-drop selection.
preventDocumentDropbooleantruePrevents an outside drop from navigating the document.
directorybooleanfalseAllows directory selection in supported browsers.
capture"user" | "environment"—Selects a device camera for media capture.
disabled / readOnly / required / invalidbooleanfalseAvailability, form, and validation states.
namestring—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

PartElementDescription
FileUpload.RootdivOwns file selection, constraints, and accepted or rejected state.
FileUpload.RootProviderdivUses an external useFileUpload machine.
FileUpload.LabellabelNames the file input.
FileUpload.DropzonedivAccepts pointer clicks and file drops. Use disableClick with a nested Trigger.
FileUpload.TriggerbuttonOpens the native file picker.
FileUpload.ItemGroupulGroups accepted or rejected files.
FileUpload.ItemliProvides one File to its item parts.
FileUpload.ItemPreview / ItemPreviewImagediv / imgShows generic or image-specific file media.
FileUpload.ItemName / ItemSizeTextdivShows the file name and localized size.
FileUpload.ItemDeleteTriggerbuttonRemoves one file. A default close icon is included.
FileUpload.ClearTriggerbuttonRemoves all accepted files.
FileUpload.HiddenInputinputProvides native form and file-picker behavior.
FileUpload.ContextslotExposes accepted files, rejected files, limits, and machine actions.

Events

EventPayloadDescription
update:acceptedFilesFile[]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

AttributeValueDescription
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

ExportDescription
FileUploadCompound namespace and root component.
FileUpload.Root / RootProviderMachine-owned and provider-owned roots.
FileUpload.Label / Dropzone / Trigger / HiddenInputFile-selection parts.
FileUpload.ItemGroup / Item / Item* / ClearTriggerAccepted and rejected file-list parts.
FileUpload.ContextScoped access to current machine state and actions.
FileUploadProps / FileUploadEmitsKappa root props and Vue event contracts.
FileUploadApi / FileUploadContextValueProvider and context API types.
useFileUpload / useFileUploadContext / fileUploadAnatomyArk UI hook and anatomy re-exports.