跳到主要内容

FileInput

Read-only field with a folder icon that opens the file dialog and accepts a drop. The whole field is the drop target, and the names of the chosen files are written into it.

FileInputFileInput

Use this when / not when​

  • Use where a file is one field of a form, beside the other fields — an import, an avatar, a certificate.
  • Not for a large drop area. Dropzone is the panel-sized one; this is an input-sized control.
  • Not to show upload progress. isLoading is a spinner in place of the icon and nothing more; there is no percentage and no cancel.
  • Not when you need the file list to be editable. The field is read-only and shows every name joined with commas, with no way to remove one.

Import​

import { FileInput } from "@onlyoffice/apps-ui-kit/components/file-input";

Also exported from the root barrel @onlyoffice/apps-ui-kit.

FileInputProps is not exported — type a wrapper's props yourself.

Needs ThemeProvider from @onlyoffice/apps-ui-kit/providers/theme, and TranslationProvider from @onlyoffice/apps-ui-kit/providers/translation for the message it raises when a file is rejected. That message is a toast, so mount Toast once in your app or it goes nowhere.

Stories​

Default​

The field as a form shows it before anything is chosen; change any prop live in the Controls panel below.

DefaultDefault

Sizes​

Pick the size that matches the other fields of the form: each one also sets the field's width and the size of its icon box (size).

SizesSizes

States​

Use these to tell the user about the chosen file: Error state and Warning state recolour the border (hasError, hasWarning), Disabled greys the field and ignores clicks (isDisabled), and Loading shows a spinner in place of the icon (isLoading).

StatesStates

With Accept Filter​

Limit the field to the types the host can handle: the picker offers only these extensions, and a dropped file of another type is refused with an error toast (accept).

With Accept FilterWith Accept Filter

Scaled Input​

Use it when the field should line up with a full-width form column: it stretches to the width of its container (scale).

Scaled InputScaled Input

With Button​

Use a labelled button when an icon alone would not tell the user what the field does: the button takes the place of the icon and grows with the field's size (buttonLabel). The field is given the full width of its container here (scale), because at a fixed size the button is laid out past the field's own width.

With ButtonWith Button

Document Icon​

Use the document icon when the field takes a single document rather than any file: Folder icon is the default, Document icon the alternative (isDocumentIcon).

Document IconDocument Icon

With Path​

Use this when the file comes from somewhere other than the device, such as a folder picker of the host's own: the field shows the path it is given (fromStorage, path), and a click on it or its icon calls the host instead of opening the file picker (onClick) — click it and watch the Actions panel.

With PathWith Path

Css Customization​

Every overridable variable set on one wrapper -- the variables are listed under CSS variables on this page.

  • Choose file — the border, background, text and radius variables; hover it for --file-input-hover-border and press it for --file-input-focus-border
  • Warning state — --file-input-warning-border (hasWarning)
  • Error state — --file-input-error-border (hasError)
  • Disabled — --file-input-disabled-border and --file-input-placeholder-color (isDisabled)
Css CustomizationCss Customization

Minimal example​

onInput gives you one File or an array, depending on how many were chosen.

import { useState } from "react";
import { FileInput } from "@onlyoffice/apps-ui-kit/components/file-input";
import { InputSize } from "@onlyoffice/apps-ui-kit/components/text-input";

export function AttachmentField() {
const [names, setNames] = useState<string[]>([]);

return (
<FileInput
size={InputSize.base}
placeholder="Choose a file"
scale
aria-label="Attachment"
onInput={(file) => {
const files = Array.isArray(file) ? file : [file];
setNames(files.map((one) => one.name));
}}
/>
);
}

Props​

PropertyTypeDescription
sizeInputSizeHeight of the field, which also picks the icon and button sizes: base and middle take a 15px icon, large a 16px one. Default: InputSize.base.
accept?string[]Extensions and MIME types the dialog offers and a drop accepts, such as [".pdf", "image/*"]. The default [""] matches only files with no MIME type, so pass it whenever the field is used. Default: [""].
aria-description?stringAccessible description of the control.
aria-label?stringAccessible name of the control. There is none without it.
buttonLabel?stringRenders a button with this label in place of the icon.
className?stringApplied to the outermost element.
data-test-id?stringdata-testid of the outermost element. Default: "file-input".
fromStorage?booleanTurns the control into a button that picks from somewhere else: the hidden file input is not rendered, path is displayed, and clicks go to onClick. Default: false.
hasError?booleanWhether the field is drawn in its error colours. Default: false.
hasWarning?booleanWhether the field is drawn in its warning colours. Default: false.
id?stringApplied to the hidden <input type="file">, not to the wrapper.
idButton?stringApplied to the outermost element.
isDisabled?booleanWhether the field is greyed and a click no longer opens the file dialog. A drop is still accepted, and Enter or Space still opens the dialog. Default: false.
isDocumentIcon?booleanWhether the icon is a document rather than a folder. Default: false.
isLoading?booleanWhether a spinner replaces the icon. It also disables the field, and it is ignored when buttonLabel is set, since the button has no loading form. Default: false.
isMultiple?booleanWhether several files may be chosen at once. Default: true.
name?stringIgnored. Nothing reads this prop.
onClick?(e: React.MouseEvent) => voidCalled when the field or the icon is clicked — but only while fromStorage is set. Without it the click opens the file dialog and this never fires.
onInput?(file: File | File[]) => voidCalled with the chosen files: a single File when one was picked, an array when several were. Check for an array before reading .name.
path?stringText shown in the field instead of the chosen file names, with fromStorage.
placeholder?stringPlaceholder of the read-only field, shown until a file is chosen.
scale?booleanWhether the field stretches to fill its container. Default: false.
style?React.CSSPropertiesApplied to the outermost element.

Enums​

EnumMembers
InputSizebase, middle, large

Recipes​

Loading​

isLoading swaps the icon for a spinner and disables the field. It does not survive buttonLabel: the button form has no loading state.

import { useState } from "react";
import { FileInput } from "@onlyoffice/apps-ui-kit/components/file-input";
import { InputSize } from "@onlyoffice/apps-ui-kit/components/text-input";

export function UploadField({
upload,
}: {
upload: (file: File) => Promise<void>;
}) {
const [isUploading, setIsUploading] = useState(false);

return (
<FileInput
size={InputSize.base}
placeholder="Choose a file"
scale
isMultiple={false}
isLoading={isUploading}
aria-label="Upload"
onInput={async (file) => {
setIsUploading(true);
await upload(Array.isArray(file) ? file[0] : file);
setIsUploading(false);
}}
/>
);
}

Disabled / read-only​

isDisabled greys the field and stops a click from opening the dialog. It does not stop a drop, or Enter and Space on the focused field (see below). The field itself is always read-only — nothing can be typed into it.

import { FileInput } from "@onlyoffice/apps-ui-kit/components/file-input";
import { InputSize } from "@onlyoffice/apps-ui-kit/components/text-input";

export function LockedAttachment() {
return (
<FileInput
size={InputSize.base}
placeholder="Ask an administrator"
scale
isDisabled
aria-label="Attachment"
/>
);
}

Picking from somewhere else​

fromStorage turns the control into a button: no hidden file input is rendered, path is shown in the field, and clicks are handed to onClick so you can open a picker of your own.

import { useState } from "react";
import { FileInput } from "@onlyoffice/apps-ui-kit/components/file-input";
import { InputSize } from "@onlyoffice/apps-ui-kit/components/text-input";

export function StorageField({ openPicker }: { openPicker: () => void }) {
const [path] = useState("");

return (
<FileInput
size={InputSize.base}
placeholder="Choose from storage"
scale
fromStorage
path={path}
aria-label="File from storage"
onClick={openPicker}
/>
);
}

Behaviour the types don't state​

  • A rejected file raises a toast, not a callback. Anything accept turns away produces the kit's "not supported format" message through toastr, so Toast has to be mounted somewhere and TranslationProvider set up. Your code is not told.
  • onInput's argument changes shape with the count. One file arrives as a File, two or more as File[]. isMultiple defaults to true, so this is the common case, not the rare one.
  • accept needs setting. It is handed to react-dropzone 11, which reads an array of extensions and MIME types — [".pdf", ".docx"], ["image/*"] — for both the dialog and the drop. The default of [""] matches only a file with no MIME type, so a PDF or an image chosen with the default is rejected with the toast.
  • isDisabled and isLoading only stop the click. They reach react-dropzone as noClick, so a dropped file is still accepted and onInput still fires, and Enter or Space on the focused field still opens the dialog.
  • onClick only works with fromStorage. Without it the handler is never attached, because the click has to reach the hidden file input instead.
  • id and idButton go to different elements: id to the hidden <input type="file">, idButton to the wrapper. The one you want for a label is usually idButton.
  • The component is memoised on a deep comparison. A new object or array passed inline — accept, style — does not force a re-render the way it normally would, and a handler that closes over stale state will keep doing so until something else changes.
  • The field shows names, not paths, joined with ", " when there are several, and there is no way to clear it from the outside short of remounting.
  • The width is fixed per size unless scale is set: 173px for base, 300px for middle, 550px for large. The icon box at the end is 30 × 30px for base and middle and 48 × 42px for large.
  • isLoading also disables the field, and both it and isDisabled are passed to the inner TextInput.
  • name is dead — the prop is declared and nothing reads it.

CSS variables​

Each is read with a fallback to a theme value, so set any of them on an ancestor. The field is the inner TextInput; the icon box is the bordered square at its end.

VariableDefaultEffect
--file-input-bordertheme valueBorder colour of the field and the icon box at rest
--file-input-hover-bordertheme valueBorder colour of the icon box while the pointer is over the control
--file-input-focus-bordertheme valueBorder colour of the icon box while the control is pressed
--file-input-disabled-bordertheme valueBorder colour of the field and the icon box under isDisabled
--file-input-warning-bordertheme valueBorder colour of the field and the icon box under hasWarning
--file-input-error-bordertheme valueBorder colour of the field and the icon box under hasError
--file-input-placeholder-colortheme valuePlaceholder colour of the field under isDisabled or isLoading
--file-input-radius3pxRadius of the icon box's outer corners

The field's background, text colour and radius are TextInput's own --text-input-bg, --text-input-color and --text-input-radius, which reach it from the same ancestor. The icon's colour cannot be changed from outside: the icon sets --icon-button-color on itself, so a wrapper's value never reaches it.

Accessibility​

  • The wrapper is a <div> with role="button" and tabIndex="0", both from react-dropzone, so it is reached with Tab and announced as a button. Enter and Space on it open the file dialog.
  • There is no accessible name unless you pass aria-label. The placeholder is not a label. aria-label and aria-description are both applied to the wrapper.
  • aria-disabled is "true" under isDisabled and "false" otherwise. The wrapper stays in the tab order while disabled, and Enter and Space still open the dialog.
  • A rejected file is announced only through a toast, which may be missed entirely.

Test ids​

Elementdata-testid
The wrapperfile-input, or data-test-id
The hidden inputupload-click-input
The iconicon-button
  • Dropzone — the panel-sized drop area.
  • TextInput — the field this shows names in.
  • Toast — where a rejected file is reported.