File Upload
Drag-and-drop file uploader. Default dropzone, compact row, avatar uploader, and dropzone with file list.
Overview
The File Upload component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.
Why this exists
File uploads are a common friction point in forms. A well-designed upload zone with drag-and-drop, file preview, and clear error states reduces user anxiety.
Mental model
A file upload zone accepts files via click or drag-and-drop. It validates type and size, previews accepted files, and surfaces errors clearly.
When to use
When not to use
Basic example
The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.
Default
Compact
Avatar uploader
Profile photo
JPG or PNG, square, at least 200x200 px
With file list
-
photo-of-the-team.jpg
62% -
contract-v2.pdf
2.4 MB · Uploaded
-
huge-file.zip
Exceeds 100 MB limit
import { useState } from 'react';
import { FileUpload, FileUploadList, FileUploadItem } from '@byteion/react';
export default function FileUploadDemo() {
const [files, setFiles] = useState<File[]>([]);
return (
<div className="grid gap-4 p-6">
<FileUpload
accept="image/*"
multiple
onFiles={(list) => setFiles([...files, ...list])}
hint="PNG or JPG up to 5MB"
/>
<FileUploadList>
{files.map((f) => (
<FileUploadItem key={f.name} name={f.name} size={f.size} state="success" />
))}
</FileUploadList>
</div>
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { FileUpload, FileUploadList, FileUploadItem } from '@byteion/vue';
const files = ref<File[]>([]);
function onFiles(list: File[]) { files.value = [...files.value, ...list]; }
</script>
<template>
<div class="grid gap-4 p-6">
<FileUpload accept="image/*" multiple hint="PNG or JPG up to 5MB" @files="onFiles" />
<FileUploadList>
<FileUploadItem v-for="f in files" :key="f.name" :name="f.name" :size="f.size" state="success" />
</FileUploadList>
</div>
</template>
Variants
Each variant communicates a different meaning. Choose based on priority, not aesthetics.
Single default variant.
Sizes
Size should match the importance and context of the component.
- md — md size.
States
This component reacts seamlessly to the following interaction and data states:
- idle — idle state.
- hover — Pointer is over the element — visual feedback via color or shadow.
- uploading — uploading state.
- complete — complete state.
- error — Validation failed — red border and error message.
- default — Resting appearance before interaction.
- focus — Keyboard or tab focus — shows ring indicator for accessibility.
- active — Currently selected or in use.
- disabled — Non-interactive — grayed out, cursor not-allowed.
Anatomy
Usage guidelines
- Accept drag-and-drop and click-to-browse.
- Validate file type and size before upload.
- Show a preview for image files.
Do and Don't
Do
- Support drag-and-drop.
- Validate before upload.
- Show file previews.
Don't
- Do not accept files without validation.
- Do not hide the upload progress.
- Do not use without a remove option.
Accessibility
Hidden native input is triggered by clicking the zone. Zone must have a descriptive label.
Role:input (file)
ARIA Attributes
aria-label
Keyboard Navigation
- Tab
- Enter
- Space
Customization
Border and hover colors follow design tokens.
Compose with React cva()
Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.
import { cva, cn, type VariantProps } from '@byteion/react';
const myFileUpload = cva('base classes…', {
variants: {
accent: { soft:'border-primary-200', loud:'border-primary-500' },
},
defaultVariants: { accent:'soft' },
});
type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof myFileUpload>;
Compose with Vue cva()
Use the exportedcva() +cn() utilities inside standard Vue setup blocks.
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';
interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();
const myFileUpload = cva('base classes…', {
variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});
const cls = computed(() => myFileUpload({ accent: props.accent, class: attrs.class as string | undefined }));
</script>
Runtime events
Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.
HTML / JS events
bi:file-upload:addbi:file-upload:cancelbi:file-upload:errorbi:file-upload:progressbi:file-upload:removebi:file-upload:retrybi:file-upload:success
React callbacks
onFilesAdded(files: File[]) => voidonRemove() => voidonRetry() => void
Vue emits
filesAdded[files: File[]]
Remote / AJAX
XHR upload
Add data-bi-upload-url for automatic XMLHttpRequest uploads. Without it, FileUpload remains a local file picker with validation, drag-and-drop, remove, and list rendering.
Attributes
data-bi-upload-urldata-bi-methoddata-bi-field-namedata-bi-max-sizedata-bi-accept
Runtime events
bi:file-upload:addbi:file-upload:progressbi:file-upload:successbi:file-upload:errorbi:file-upload:removebi:file-upload:cancelbi:file-upload:retry
Accessibility: loading sets aria-busy="true"; empty states use role="status"; error states use alert/status messaging where the runtime renders remote status rows.
API Reference
Uses hidden native input. Zone is a styled label.
HTML Data Attributes
No custom data attributes required.
React Props · FileUploadProps
Extracted directly fromreact/src/components/FileUpload.tsx.
export interface FileUploadProps
extends Omit<HTMLAttributes<HTMLLabelElement>, 'children' | 'onChange' | 'title'> {
/** Fired when files are added via the picker or drop. */
onFilesAdded?: (files: File[]) => void;
/** Allow multiple files. */
multiple?: boolean;
/** Accept attribute (e.g. "image/*", ".pdf"). */
accept?: string;
/** Disable the upload zone. */
disabled?: boolean;
/** Visual variant. */
variant?: 'default' | 'compact';
/** Headline above the description. */
title?: ReactNode;
/** Sub-line shown beneath the title (e.g. "PNG, JPG, PDF up to 10 MB"). */
description?: ReactNode;
/** Optional id passed to the hidden input. */
id?: string;
/** Render any extra content inside the zone (replaces the default body). */
children?: ReactNode;
}
Vue Props · FileUploadProps
Extracted directly fromvue/src/components/FileUpload.vue.
export interface FileUploadProps {
/** Allow multiple files. */
multiple?: boolean;
/** Accept attribute (e.g. "image/*", ".pdf"). */
accept?: string;
/** Disable the upload zone. */
disabled?: boolean;
/** Visual variant. */
variant?: 'default' | 'compact';
/** Headline above the description. */
title?: string;
/** Sub-line shown beneath the title. */
description?: string;
/** Optional id passed to the hidden input. */
id?: string;
}
Developer tips
Common mistakes
Production usage tips
data-bi-* attributes instead of styling hooks.
Premium examples
Advanced patterns available in Byteion Pro:
Related components
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.
Explore Pro Components