FileUpload
Overviewimport { FileFormatIcon, FileUploadArea, FileUploadCard, ImageUpload, ImageUploadShape } from '@nili/ui';This is the library’s own overview page, rendered live. Switch the language or the theme in the header and it follows.
Upload Area
The drop target. onFiles receives a File array whether they were dropped or chosen from the dialogue, so there is one code path to handle. It is a real input underneath, so keyboard and screen-reader users get the picker too.
import { FileUploadArea } from "@nili/ui"
export function Example() {
return (
<FileUploadArea
multiple
accept="image/*,application/pdf"
label="Upload files"
title="Choose a file or drag it here"
description="PNG, JPG or PDF, up to 10 MB."
onFiles={(files) => upload(files)}
/>
)
}Upload Area states
invalid for a rejected file — wrong type, too large — and disabled while a quota is exhausted or a form is submitting.
import { FileUploadArea } from "@nili/ui"
export function Example() {
return (
<>
<FileUploadArea label="Upload" onFiles={onFiles} />
<FileUploadArea label="Upload" invalid onFiles={onFiles} />
<FileUploadArea label="Upload" disabled onFiles={onFiles} />
</>
)
}File Card
One uploaded file: its format icon, name, size and state. progress drives the bar; onRetry appears with an error, onRemove with anything. Both need a label, since the buttons are icons.
import { FileUploadCard } from "@nili/ui"
export function Example() {
return (
<>
<FileUploadCard
fileName="annual-report.pdf"
fileSize="2.4 MB"
status="progress"
progress={62}
onRemove={cancel}
removeLabel="Cancel upload"
/>
<FileUploadCard
fileName="photo.png"
fileSize="840 KB"
status="success"
onRemove={remove}
removeLabel="Remove file"
/>
<FileUploadCard
fileName="archive.zip"
fileSize="18 MB"
status="error"
errorMessage="File is larger than 10 MB."
onRetry={retry}
retryLabel="Try again"
onRemove={remove}
removeLabel="Remove file"
/>
</>
)
}File Format Icon
The glyph is derived from the file name, so a list of mixed uploads colours itself. Override it with color when the extension is not the thing you want to signal.
import { FileFormatIcon } from "@nili/ui"
export function Example() {
return (
<>
<FileFormatIcon fileName="report.pdf" />
<FileFormatIcon fileName="sheet.xlsx" />
<FileFormatIcon fileName="photo.png" size="xs" />
<FileFormatIcon fileName="anything.bin" color="purple" />
</>
)
}Image Upload
The preview-first variant, for an avatar, a company mark or a cover. shape sets the frame and the crop the preview implies; alignment puts the controls under the image or beside it.
import { ImageUpload, Button } from "@nili/ui"
export function Example() {
return (
<>
<ImageUpload
shape="avatar"
label="Profile photo"
hint="JPG or PNG, at least 256×256."
onFiles={(files) => upload(files[0])}
actions={<Button size="xs" appearance="stroke">Choose file</Button>}
/>
<ImageUpload shape="16:9" alignment="horizontal" label="Cover" onFiles={onFiles} />
</>
)
}FileCard
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| status | progress | success | error | progress |
FileFormatIcon
extends Omit< ComponentPropsWithoutRef<'span'>, 'color' >
Variant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| color | red | orange | yellow | green | teal | sky | blue | purple | pink | gray | gray | |
| size | md | xs | md |
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| fileName* | string | — |
FileProgress
internalFileProgressBar
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| status | progress | success | error | progress |
FileUploadArea
extends Omit< ComponentPropsWithoutRef<'label'>, 'onDrop' | 'onChange' | 'title' >
Variant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| disabled | boolean | false | |
| invalid | boolean | false | |
| dragging | 'true' | 'false' | false |
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| onFiles* | (files: File[]) => void | — | |
| accept | string | — | |
| multiple | boolean | false | |
| title | ReactNode | Choose a file or drag it here | |
| description | ReactNode | — | |
| icon | ReactNode | — | |
| label | string | Upload file | Accessible name for the hidden file input. |
FileUploadCard
extends Omit< ComponentPropsWithoutRef<'div'>, 'children' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| fileName* | string | — | |
| fileSize | string | — | Pre-formatted — size formatting is a locale decision. |
| status | error | success | progress | progress | |
| progress | number | 0 | 0–100. Ignored unless `status` is `'progress'`. |
| errorMessage | ReactNode | — | |
| onRemove | () => void | — | |
| onRetry | () => void | — | |
| removeLabel | string | Remove | |
| retryLabel | string | Retry |
ImageUpload
extends Omit< ComponentPropsWithoutRef<'div'>, 'onChange' >
Variant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| shape | avatar | company | square | 4:3 | 16:9 | square |
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| alignment | vertical | horizontal | vertical | |
| src | string | — | Preview URL. Absent means the Figma "Empty" state. |
| onFiles* | (files: File[]) => void | — | |
| accept | string | image/* | |
| label | string | Upload image | |
| hint | ReactNode | — | |
| actions | ReactNode | — | |
| alt | string |
ImageUploadRoot
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| alignment | vertical | horizontal | vertical |