Skip to Content

File upload

Allow the user to upload files from their system.

import * as React from 'react';
import { FileUpload, FileUploadCard } from '@itwin/itwinui-react';

export default () => {
  const [files, setFiles] = React.useState([]);
  return (
    <FileUpload
      onFileDropped={(files) => {
        const fileArray = Array.from(files);
        setFiles(fileArray);
      }}
    >
      <FileUploadCard
        files={files}
        onFilesChange={(files) => setFiles(files)}
      />
    </FileUpload>
  );
};

File uploading is a common interaction that may happen in various different ways depending on the context. The general pattern involves:

  1. Letting the user know they can upload content.
  2. Displaying upload status progress.
  3. Confirming the upload’s status upon completion.

This page is mainly concerned with the first part of the process (before the upload actually starts). For showing progress, you can use a dialog with a progress indicator inside it.

Usage

iTwinUI provides a few different components for working with file uploads.

FileUpload

FileUpload is a simple wrapper component that can be used to add drag-and-drop behavior to any component.

import * as React from 'react';
import { FileUpload, LabeledInput } from '@itwin/itwinui-react';

export default () => {
  const [files, setFiles] = React.useState([]);
  return (
    <FileUpload
      dragContent='Drop file to upload'
      onFileDropped={(files) => {
        setFiles(Array.from(files));
        console.log(`${files.length} files uploaded`);
      }}
    >
      <LabeledInput
        aria-label='Message'
        placeholder='Type a message'
        className='demo-labeled-input'
        message={
          files.length
            ? `Attached: ${files.map((f) => f.name)}`
            : 'Drag files to attach'
        }
      />
    </FileUpload>
  );
};

The onFileDropped prop is called with two arguments: the file list and the underlying "drop" event. The file list can suffice for simple file uploads, while the event contains more data (for example, event.dataTransfer.items) that can be useful if you have a more complex use case, such as directory uploads.

FileUploadCard

FileUploadCard consists of a card-like UI that uses a file input underneath. The empty state (when no file is uploaded) is specified using the emptyCard prop which is set to use FileEmptyCard by default.

FileUploadCard includes the following sub-components:

  • FileUploadCard.Action
  • FileUploadCard.Info
  • FileUploadCard.Title
  • FileUploadCard.Description
  • FileUploadCard.Icon
  • FileUploadCard.Input
  • FileUploadCard.InputLabel

FileEmptyCard includes the following sub-components:

  • FileEmptyCard.Icon
  • FileEmptyCard.Text

The following example shows how some of these parts can be customized.

import * as React from 'react';
import {
  FileUpload,
  FileUploadCard,
  FileEmptyCard,
  IconButton,
} from '@itwin/itwinui-react';
import {
  SvgSmileySadVery,
  SvgSmileyHappyVery,
  SvgClose,
} from '@itwin/itwinui-icons-react';

export default () => {
  const inputRef = React.useRef(null);
  const [files, setFiles] = React.useState([]);
  const emptyCard = (
    <FileEmptyCard>
      <FileEmptyCard.Icon>
        <SvgSmileySadVery />
      </FileEmptyCard.Icon>
      <FileEmptyCard.Text>
        <FileUploadCard.InputLabel>Custom Label Text</FileUploadCard.InputLabel>
        <div>Custom Description Text</div>
      </FileEmptyCard.Text>
    </FileEmptyCard>
  );
  return (
    <FileUpload
      onFileDropped={(files) => {
        const fileArray = Array.from(files);
        setFiles(fileArray);
        console.log(`${files.length} files uploaded`);
      }}
    >
      <FileUploadCard
        files={files}
        onFilesChange={(files) => setFiles(files)}
        emptyCard={emptyCard}
        input={<FileUploadCard.Input name={'my-file'} ref={inputRef} />}
      >
        <FileUploadCard.Icon>
          <SvgSmileyHappyVery />
        </FileUploadCard.Icon>
        <FileUploadCard.Info>
          <FileUploadCard.Title>Custom File Name</FileUploadCard.Title>
          <FileUploadCard.Description>
            Custom File Description
          </FileUploadCard.Description>
        </FileUploadCard.Info>
        <FileUploadCard.Action>
          <IconButton
            onClick={() => {
              setFiles([]);
            }}
            styleType={'borderless'}
          >
            <SvgClose />
          </IconButton>
        </FileUploadCard.Action>
      </FileUploadCard>
    </FileUpload>
  );
};

Usage guidelines

Chat

The user can either click on an attach icon or drag & drop a file over the text area. The text area will visibly change when a file is dragged above it. Once the user drops the file above the text area a progress indicator appears. Depending on the applications needs, one of two things can happen.

The file could immediately be uploaded and added to the chat stream without any additional input from the user. Because there is no way to stop / cancel the upload it is a good idea to allow users to retract / delete messages after they have been sent.

The second option is the file could be queued within the message itself. This gives the user a chance to remove the file if they have changed their mind or accidentally uploaded the wrong file. This method does require an additional step from the user but is typically safer than the previous method.

Table

The user can either click on an upload button or drag & drop a file over the table. The table will visibly change when a file is dragged above it. Once the user drops the file above the table two things happen simultaneously.

First, the file appears at the top of the table. The files that are being uploaded are visually different from the rest of the files so the user knows they are an upload in progress. As the files upload, they visually change to match the rest of the files. Once the file is finished uploading, it remains at the top of the table despite sort filters applied. Upon any sort of table refresh (page refresh, a new column sort, etc) the most recently uploaded files that were at the top are then sorted amongst the rest of the files.

The second thing that happens when a file is uploaded is the upload tracker appears in the bottom right. This allows the user to track their upload progress if they navigate further down the page when infinite scrolling is enabled / navigate to a deeper page when pagination is enabled.

In some cases, there may be a table with no files uploaded yet. In a situation like this, we display an empty state explaining how to populate the table.

Props

Prop Description Default
dragContent
Content shown over children when file is being dragged onto the component. Should always be used when drag content differs from children being wrapped.
ReactNode
onFileDropped
Callback fired when files are dropped onto the component.
The first argument is the files list, and the second argument is the underlying "drop" event.
(files: FileList, event: DragEvent<Element>) => void
children
Component to wrap FileUpload around. Either pass FileUploadCard (for default state) or a different component to wrap.
ReactNode
contentProps
Allows for custom prop to be passed for content.
DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
as
"symbol" | "object" | "div" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | "canvas" | ... 158 more ... | FunctionComponent<...>