Tessina UI
Tessina UI
GitHubIntroduction
InstallationUsageTheming
Components
ButtonIconButtonLinkSpinnerBadgeAvatarStatusChipShortcutSkeletonSurfaceProgressMeterRating
LabelFieldFieldsetCheckboxRadioSwitchSliderSelectComboboxSearchTextareaNumberFieldDate PickerTime PickerOTP InputFile UploadCalendar
AlertBannerToastTooltip
TabsAccordionCollapsibleBreadcrumbPaginationStepperSegmentedControlButtonGroupToggleButtonToggleGroupToolbarNavigation MenuMenubarBottom NavSidebar
Split ButtonFABDropdown MenuContextMenuCommandPopoverHoverCard
ContainerStackFlexGridAspectRatioSpacerCardCarouselDividerScroll Area
Table
ChatBubblePromptInputCodeBlock
ModalAlertDialogDrawerAction SheetTop Header MobileTop Header DesktopEmptyStateForm
Contributing
ComponentsForm Blocks

File Upload

Drag-and-drop file upload with file list, progress, validation, four visual variants, five sizes, intent colours, and per-file status (idle/uploading/success/error).

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { FileUpload, FileUploadItem, FileUploadList } from "@tessinaui/ui";
{/* Single file */}
<FileUpload onValueChange={(files) => console.log(files)} />

{/* Multiple files */}
<FileUpload multiple maxFiles={5} />

{/* Restrict to images, 5 MB max */}
<FileUpload accept="image/*" maxSize={5 * 1024 * 1024} />

{/* Custom title / description / button */}
<FileUpload
  title="Front of your ID document"
  description="Drag and drop a file less than 5MB"
  actionLabel="Or select file"
/>

{/* Variants */}
<FileUpload variant="card" />
<FileUpload variant="dashed" />
<FileUpload variant="filled" />
<FileUpload variant="minimal" />

{/* Intents */}
<FileUpload intent="error" />
<FileUpload intent="success" />

{/* States */}
<FileUpload loading />
<FileUpload disabled />
<FileUpload skeleton />

{/* Controlled */}
<FileUpload value={files} onValueChange={setFiles} />

{/* Rejected file callback */}
<FileUpload
  maxSize={1024 * 1024}
  onFileRejected={(r) => alert(r.message)}
/>

Showcase

Variants

VariantDescription
cardSolid filled card surface — best for prominent primary upload zones
dashedDashed border drop-zone — the classic "drop files here" look
filledFilled background with a solid border — works well with intent tinting
minimalSolid 1px border, transparent background — discreet inline upload

Props

<FileUpload>

PropTypeDefaultDescription
acceptstring—Comma-separated MIME types or extensions (e.g. "image/*,.pdf")
maxSizenumber—Max bytes per file
maxFilesnumber1Max files in the list
multiplebooleanmaxFiles > 1Whether the file picker allows multi-select
valueUploadedFile[]—Controlled file list
defaultValueUploadedFile[][]Uncontrolled initial list
onValueChange(files) => void—Fires when the list changes
onFileAdded(file) => void—Fires per accepted file
onFileRejected(rejection) => void—Fires per file that fails validation (size / type / max-files)
onFileRemoved(file) => void—Fires when a file is removed
variantcard | dashed | filled | minimaldashedVisual style
sizexs | sm | md | lg | xlmdPadding / typography scale
intentnone | error | warning | success | infononeColour intent
roundednone | sm | md | lg | fulllgCorner radius
disabledbooleanfalseDisables the dropzone and picker
readOnlybooleanfalseDisables add/remove but renders normally
loadingbooleanfalseShows a spinner inside the dropzone with "Uploading…"
skeletonbooleanfalseRenders a pulse placeholder version
hideListbooleanfalseHide the file list rendered below
iconReactNode<Upload />Custom icon inside the circular badge
titleReactNode—Heading text
descriptionReactNode—Subtitle text
actionLabelReactNode"Or select file"Button label
dirltr | rtlltrText direction

<FileUploadItem>

PropTypeDescription
fileUploadedFileThe file metadata + status
sizeFileUploadSizeInherits from parent unless overridden
roundedFileUploadRoundedInherits from parent unless overridden
onRemove() => voidRemove button handler — pass undefined to hide it
onRetry() => voidRetry button handler — only visible in error status

UploadedFile

{
  id:        string;
  file:      File;
  name:      string;
  size:      number;
  type:      string;
  preview?:  string;            // object URL for images
  progress?: number;            // 0–100
  status?:   "idle" | "uploading" | "success" | "error";
  error?:    string;
}

File item states

The status of each entry in the list drives its appearance:

  • idle — file picked but no upload started; name + size + remove button
  • uploading — animated progress bar, percentage, and a spinner instead of the remove button
  • success — green check + "Uploaded"; remove still allowed
  • error — red alert + error message; retry button appears when onRetry is provided

Validation

FileUpload validates each incoming file against accept and maxSize. Rejected files don't enter the list — they fire onFileRejected with a { file, reason, message } payload where reason is one of:

  • "type" — extension or MIME type didn't match accept
  • "size" — file is larger than maxSize
  • "max-files" — adding would exceed maxFiles

Accessibility

  • The drop-zone is a role="button" with tabIndex={0} and a meaningful aria-label
  • Enter / Space opens the native file picker
  • Each item exposes aria-label="Remove file" / "Retry upload" on its action buttons
  • Object URLs created for image previews are revoked on unmount and on file removal

OTP Input

One-time password input with individual digit cells. Supports three visual variants, five sizes, five intents, masking, separator groups, numeric/alphanumeric/alphabetic input types, and full RTL/LTR support.

Calendar

Date picker calendar built on react-day-picker v9. Supports single, range, and multiple selection modes, RTL, week numbers, disabled dates, multi-month view, and an optional footer slot.

On this page

PlaygroundInstallationUsageShowcaseVariantsProps<FileUpload><FileUploadItem>UploadedFileFile item statesValidationAccessibility