File Upload is a standalone package component plus the KuiUploadFile type for controlled state.

file-upload.ts
import { KuiFileUpload, type KuiUploadFile } from '@kikita-labs/ui';

Bind the files model and drive upload transport from your own service.

Drag files here or choose a file
PNG, JPG, or PDF up to 10 MB
<div class="basic-file-upload-example">
  <kui-file-upload
    acceptLabel="PNG, JPG, or PDF up to 10 MB"
    [accept]="['image/png', 'image/jpeg', 'application/pdf']"
    [maxSize]="10 * 1024 * 1024"
    [maxCount]="3"
    [(files)]="files"
    (retry)="handleRetry($event)"
  />
</div>

The component validates picks, but never uploads bytes by itself.

  • Picked files enter the model as pending, or error when client validation fails.
  • Consumers update each entry to uploading, success, or error as their transport runs.
  • The retry output only reports intent; restart the upload and write new state yourself.
  • Use compact variant for dense forms and dropzone when drag-and-drop is useful.
  • Files rejected by client validation carry errorKind (type or size), so their error text follows the active messages; static maxSize and maxCount values are coerced and an invalid or non-positive value omits the limit.

Set defaults.fileUpload once for the application or for a subtree. Each option resolves as local input > defaults.fileUpload.<option> > built-in default.

import { provideKikitaUi } from '@kikita-labs/ui';

export const appConfig: ApplicationConfig = {
  providers: [
    provideKikitaUi({
      defaults: {
        fileUpload: {
          /* options below */
        },
      },
    }),
  ],
};
NameTypeDefaultDescription
size'xs' | 'sm' | 'md' | 'lg' — Component size. Takes precedence over the global defaults.size.
variant'dropzone' | 'compact' — Default variant.
mode'single' | 'multiple' — Default mode.
removeIconKuiIconGlyph — Icon of the remove button. Takes precedence over defaults.icons.remove. See Structural Icons.

Set any of these on the component or an ancestor to restyle one part. Each token is optional: when it is not set, the part uses the role or scale token in the default column.

NameTypeDefaultDescription
--kui-file-upload-dropzone-bg-hoverCSS custom property--kui-color-surfaceDropzone background, hover
--kui-file-upload-dropzone-focus-ring-colorCSS custom property--kui-color-focusDropzone focus ring color
--kui-file-upload-dropzone-border-overCSS custom property--kui-color-primary-indicatorDropzone border color, over
--kui-file-upload-dropzone-bg-overCSS custom property--kui-color-primary-soft-bgDropzone background, over
--kui-file-upload-dropzone-border-invalidCSS custom property--kui-color-danger-indicatorDropzone border color, invalid
--kui-file-upload-dropzone-bg-invalidCSS custom property--kui-color-danger-soft-bgDropzone background, invalid
--kui-file-upload-dropzone-icon-colorCSS custom property--kui-color-text-secondaryDropzone icon color
--kui-file-upload-dropzone-icon-color-overCSS custom property--kui-color-primary-textDropzone icon color, over
--kui-file-upload-dropzone-icon-color-invalidCSS custom property--kui-color-danger-textDropzone icon color, invalid
--kui-file-upload-dropzone-text-colorCSS custom property--kui-color-textDropzone text color
--kui-file-upload-dropzone-text-emphasis-colorCSS custom property--kui-color-primary-soft-textDropzone text emphasis color
--kui-file-upload-dropzone-hint-colorCSS custom property--kui-color-text-secondaryDropzone hint color
--kui-file-upload-compact-hint-colorCSS custom property--kui-color-text-secondaryCompact hint color
--kui-file-upload-form-error-colorCSS custom property--kui-color-danger-textForm error color
--kui-file-upload-item-borderCSS custom property--kui-color-borderItem border color
--kui-file-upload-item-bgCSS custom property--kui-color-surfaceItem background
--kui-file-upload-item-focus-ring-colorCSS custom property--kui-color-focusItem focus ring color
--kui-file-upload-item-preview-bgCSS custom property--kui-color-surface-elevatedItem preview background
--kui-file-upload-item-preview-colorCSS custom property--kui-color-text-secondaryItem preview color
--kui-file-upload-item-preview-bg-pdfCSS custom property--kui-color-danger-soft-bgItem preview background, pdf
--kui-file-upload-item-preview-color-pdfCSS custom property--kui-color-danger-soft-textItem preview color, pdf
--kui-file-upload-item-preview-bg-docCSS custom property--kui-color-info-soft-bgItem preview background, doc
--kui-file-upload-item-preview-color-docCSS custom property--kui-color-info-soft-textItem preview color, doc
--kui-file-upload-item-preview-bg-zipCSS custom property--kui-color-warning-soft-bgItem preview background, zip
--kui-file-upload-item-preview-color-zipCSS custom property--kui-color-warning-soft-textItem preview color, zip
--kui-file-upload-item-name-colorCSS custom property--kui-color-textItem name color
--kui-file-upload-item-meta-colorCSS custom property--kui-color-text-secondaryItem meta color
--kui-file-upload-item-success-colorCSS custom property--kui-color-success-textItem success color
NameTypeDefaultDescription
--kui-file-upload-gapCSS custom property--kui-space-4Gap
--kui-file-upload-dropzone-padding-blockCSS custom property--kui-space-8Dropzone padding, block
--kui-file-upload-dropzone-padding-inlineCSS custom property--kui-space-6Dropzone padding, inline
--kui-file-upload-dropzone-gapCSS custom property--kui-space-2Dropzone gap
--kui-file-upload-dropzone-padding-has-filesCSS custom property--kui-space-4Dropzone padding, has files
--kui-file-upload-dropzone-text-font-sizeCSS custom property--kui-text-sm-sizeDropzone text font size
--kui-file-upload-dropzone-hint-font-sizeCSS custom property--kui-text-xs-sizeDropzone hint font size
--kui-file-upload-compact-trigger-gapCSS custom property--kui-space-3Compact trigger gap
--kui-file-upload-compact-hint-font-sizeCSS custom property--kui-text-xs-sizeCompact hint font size
--kui-file-upload-form-error-font-sizeCSS custom property--kui-text-xs-sizeForm error font size
--kui-file-upload-list-gapCSS custom property--kui-space-2List gap
--kui-file-upload-item-gapCSS custom property--kui-space-3Item gap
--kui-file-upload-item-padding-blockCSS custom property--kui-space-2Item padding, block
--kui-file-upload-item-padding-inlineCSS custom property--kui-space-3Item padding, inline
--kui-file-upload-item-radiusCSS custom property--kui-radius-mdItem corner radius
--kui-file-upload-item-preview-radiusCSS custom property--kui-radius-smItem preview corner radius
--kui-file-upload-item-preview-font-sizeCSS custom property--kui-text-2xs-sizeItem preview font size
--kui-file-upload-item-name-font-sizeCSS custom property--kui-text-sm-sizeItem name font size
--kui-file-upload-item-meta-gapCSS custom property--kui-space-2Item meta gap
--kui-file-upload-item-meta-font-sizeCSS custom property--kui-text-xs-sizeItem meta font size
--kui-file-upload-item-error-gapCSS custom property--kui-space-2Item error gap
--kui-file-upload-dropzone-padding-block-smCSS custom property--kui-space-5Dropzone padding, block sm
--kui-file-upload-dropzone-padding-inline-smCSS custom property--kui-space-4Dropzone padding, inline sm
--kui-file-upload-dropzone-gap-smCSS custom property--kui-space-1Dropzone gap, sm
--kui-file-upload-dropzone-text-font-size-smCSS custom property--kui-text-xs-sizeDropzone text font size, sm
--kui-file-upload-item-name-font-size-smCSS custom property--kui-text-xs-sizeItem name font size, sm
--kui-file-upload-dropzone-padding-block-lgCSS custom property--kui-space-12Dropzone padding, block lg
--kui-file-upload-dropzone-padding-inline-lgCSS custom property--kui-space-8Dropzone padding, inline lg
--kui-file-upload-dropzone-gap-lgCSS custom property--kui-space-3Dropzone gap, lg
--kui-file-upload-dropzone-text-font-size-lgCSS custom property--kui-text-md-sizeDropzone text font size, lg
--kui-file-upload-item-name-font-size-lgCSS custom property--kui-text-md-sizeItem name font size, lg
--kui-file-upload-dropzone-icon-sizeCSS custom property32px (24px sm, 40px lg)Dropzone icon size

Every text the component owns (accessible names, visible words, announcements) is a typed message with an English default. Override it for the application with provideKikitaUi({ messages }), or for a subtree with provideKuiMessages.

NameTypeDefaultDescription
dropzoneLabelstring — Accessible name of the dropzone. Default Upload file. Drag and drop or click to browse.
promptBeforestring — Text before the emphasised action in the dropzone. Default Drag files here or .
promptActionstring — Emphasised action in the dropzone. Default choose a file.
promptAfterstring — Text after the emphasised action in the dropzone. Default empty.
chooseFilestring — Text of the presentational button in the dropzone. Default Choose file.
attachFilestring — Text of the compact trigger. Default Attach file.
invalidTypestring — Error for a file whose type is not accepted. Default Invalid file type.
tooLargeKuiMessageFn<{ max: string; }> — Error for a file over the size limit. Default Exceeds max size (max {max}).
tooManyKuiMessageFn<{ max: number; }> — Error for too many files. Default Maximum {max} file(s), singular and plural.
uploadingKuiMessageFn<{ name: string; }> — Accessible name of a file's progress bar. Default Uploading {name}.
removeFileKuiMessageFn<{ name: string; }> — Accessible name of a file's remove button. Default Remove {name}.
donestring — Status of a finished upload. Default Done.
queuedstring — Status of a file waiting for upload. Default Queued.
retrystring — Retry action of a failed upload. Default Retry.

Inputs verified against @kikita-labs/ui v2.0.0 public typings and package docs.

NameTypeDefaultDescription
variant'dropzone' | 'compact''dropzone'Renders the full drag-and-drop zone or a compact attach button.
mode'single' | 'multiple''multiple'Controls whether a new selection replaces or appends to the file list.
acceptreadonly string[]undefinedAllowed MIME types. Omit it to accept any file type.
acceptLabelstringundefinedHuman-readable format and limit hint shown near the picker.
maxSizenumberundefinedMaximum file size in bytes.
maxCountnumberundefinedMaximum number of files in multiple mode.
size'sm' | 'md' | 'lg''md'Controls row height, thumbnail scale, and trigger density.
disabledbooleanfalseStops drag, click, and keyboard file-picking interactions.
[(files)]readonly KuiUploadFile[][]Controlled file list. Consumers own upload status and progress after picking.
(retry)KuiUploadFile-Emits when an errored item retry action is activated.
KuiUploadFile.status'pending' | 'uploading' | 'success' | 'error'-Per-file state written by the component initially and then by the consumer.
messagesPartial<KuiFileUploadMessages> | undefinedundefinedPer-instance text overrides: prompt, buttons, statuses and errors. Sizes and percentages are formatted with the locale (2.5 MB, 340 kB). They win over scoped and root messages.
KuiUploadFile.errorKind'type' | 'size'-Set by the component when client-side validation rejects a file; the displayed text then follows the active messages. Omit it for errors you set yourself.
KuiFileUploadOptionsinterface-Shape of defaults.fileUpload: size, variant, mode and removeIcon (takes precedence over defaults.icons.remove).

Drag-and-drop is optional; keyboard users still activate a native file input.

  • Keep the visible acceptLabel specific so the dropzone aria-label is meaningful.
  • Use Enter or Space on the dropzone or the compact Attach file button to open the native picker. The native file input is aria-hidden and out of the tab order: the dropzone or the compact button is the control.
  • The small Choose file label inside the dropzone is presentational (aria-hidden), not a second button, so a click on it reaches the dropzone and no interactive control is nested inside another.
  • Tab reaches file rows and actions; Delete or Backspace removes a focused file item.
  • File Upload is not a Signal Forms control; compose required state around the files model.

Consumer-visible changes of @kikita-labs/ui 2.0 for File Upload.

  • The class is KuiFileUpload (it was KuiFileUploadComponent); ng update @kikita-labs/ui renames the import.
  • Size, variant and mode fall back to defaults.fileUpload; static numeric maxSize and maxCount attributes are coerced. Sizes use Intl units (kB, it was KB), and the too-many-files error reads Maximum 1 file for one.
  • The native input and the small label inside the dropzone are aria-hidden, so the dropzone is the only interactive element and the axe nested-interactive violation is gone.
  • Rejected files carry the new errorKind field so their message follows the active messages.