Upload is a component for uploading one or more files. It shows the upload progression and status of each file. Files can be uploaded using the Upload button or via drag and drop.

Note
Work In Progress

The Java examples for this component are currently in progress. In the meantime, you can view the previous examples.

<vaadin-upload></vaadin-upload>

Drag & Drop

Upload supports drag and drop for uploading files. Multiple files can be dropped simultaneously. By default, it’s enabled on desktop and disabled on touch devices. By explicitly setting it to enabled or disabled affects both desktop and mobile devices.

<strong>Drag and drop enabled</strong>
<vaadin-upload .nodrop="${false}"></vaadin-upload>
<strong>Drag and drop disabled</strong>
<vaadin-upload nodrop></vaadin-upload>

Auto Upload

By default, files are uploaded immediately as they are added to the queue. Auto upload can be disabled, for example, to allow the user to review the list of files before initiating their upload by clicking the ▶️ button for each file. It is recommended to change the button label to indicate that uploads do not start automatically.

@query('vaadin-upload')
private upload?: UploadElement;

firstUpdated() {
  if (this.upload?.i18n) {
    this.upload.i18n.addFiles.many = 'Select Files...';
    this.upload.i18n = { ...this.upload.i18n };
  }
}

render() {
  return html`
    <vaadin-upload
      no-auto
    ></vaadin-upload>
  `;
}

Uploads can be initiated programmatically when auto upload is disabled, for example if you want to provide the user with a single button to start all uploads.

@query('vaadin-upload')
private upload?: UploadElement;

render() {
  return html`
    <vaadin-upload
      no-auto
    ></vaadin-upload>
    <p>
      <vaadin-button theme="primary" @click="${this.uploadFiles}">
        Upload All Files
      </vaadin-button>
    </p>
  `;
}

uploadFiles() {
  this.upload?.uploadFiles();
}

Upload Restrictions

You can set three types of restrictions: file format, file count and file size.

Exceptions that arise from the user violating any of the imposed restrictions aren’t shown in the UI by default. Use a File Rejected listener to catch these exceptions and, for example, a Notification to inform the user about the problem at hand and any potential solutions.

However, the user should be informed upfront about any file upload restrictions. Maximum number of files allowed, file size and format limitations should all be communicated clearly to avoid exceptions whenever possible.

File Format

Upload can be configured to only accept files of specific formats. The acceptable file formats are set using MIME type patterns or file extensions, for example "video/*", "image/tiff" or ".pdf" and "audio/mp3".

<h4>Upload report</h4>
<p>Accepted file formats: PDF (.pdf)</p>
<vaadin-upload
  accept="application/pdf,.pdf"
  max-files="1"
  @file-reject="${this.fileRejectHandler}"
></vaadin-upload>
Note
Prefer MIME-type

While MIME types are widely supported, file extensions are only implemented in certain browsers and should be avoided.

File Count

Upload does not limit the number of files that can be uploaded by default. If you set the maximum to one the native file browser prevents selecting multiple files.

render() {
  const maxFiles = 3;
  return html`
    <h4>Upload files</h4>
    <p>Maximum of ${maxFiles} files allowed</p>
    <vaadin-upload
      .maxFiles="${maxFiles}"
      @file-reject="${this.fileRejectHandler}"
    ></vaadin-upload>
  `;
}

File Size

Upload allows you to limit the file size by setting a maximum. The limit is defined in bytes. It’s unlimited by default.

render() {
  const maxFileSizeInMB = 100;
  const maxFileSizeInBytes = maxFileSizeInMB * 1024 * 1024;
  return html`
    <h4>Upload file</h4>
    <p>Maximum file size: ${maxFileSizeInMB} MB</p>
    <vaadin-upload
      max-files="1"
      .maxFileSize="${maxFileSizeInBytes}"
      @file-reject="${this.fileRejectHandler}"
    ></vaadin-upload>
  `;
}
Note
Revalidate the size limit in the server

This constraint is set on the client and is checked before contacting the server.

File Actions

Each file has a certain set of associated actions available depending on its upload state. A file always has a "Clear/Remove" button. This button cancels the upload (if applicable) and removes the file from the list.

The "Clear/Remove" button is the only available action during and after a successful upload.

import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '@vaadin/vaadin-upload/vaadin-upload';
import { applyTheme } from 'Frontend/generated/theme';

function createFakeFiles() {
  return createFakeUploadFiles([
    {
      name: 'Workflow.pdf',
      progress: 60,
      status: '19.7 MB: 60% (remaining time: 00:12:34)',
    },
    { name: 'Financials.xlsx', complete: true },
  ]);
}

@customElement('upload-clear-button')
export class Example extends LitElement {
  protected createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

  render() {
    return html`<vaadin-upload .files="${createFakeFiles()}"></vaadin-upload>`;
  }
}
Note
Remember to remove the file from the back end

The "Clear/Remove" button does not remove a successfully uploaded file from the server file system or database. It is only removed from Upload’s file list.

If an error or exception occurs Upload displays a "Retry" button that attempts to upload the file again when pressed.

import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '@vaadin/vaadin-upload/vaadin-upload';
import { applyTheme } from 'Frontend/generated/theme';

function createFakeFiles() {
  return createFakeUploadFiles([
    { name: 'Financials.xlsx', error: 'Something went wrong, please try again' },
  ]);
}

@customElement('upload-retry-button')
export class Example extends LitElement {
  protected createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

  render() {
    return html`<vaadin-upload .files="${createFakeFiles()}"></vaadin-upload>`;
  }
}

When a file is queued (auto upload disabled) it has a "Start" Button that the user must press to begin the upload process.

import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '@vaadin/vaadin-upload/vaadin-upload';
import { applyTheme } from 'Frontend/generated/theme';

function createFakeFiles() {
  return createFakeUploadFiles([
    {
      name: 'Workflow.pdf',
      status: 'Queued',
      held: true,
    },
  ]);
}

@customElement('upload-start-button')
export class Example extends LitElement {
  protected createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

  render() {
    return html`<vaadin-upload .files="${createFakeFiles()}"></vaadin-upload>`;
  }
}

Internationalization (i18n)

All of Upload’s labels and messages are configurable. For a complete list please refer to the API documentation.

render() {
  const i18n: UploadI18n = {
    dropFiles: {
      one: 'Raahaa tiedosto tähän',
      many: 'Raahaa tiedostot tähän',
    },
    addFiles: {
      one: 'Valitse tiedosto...',
      many: 'Valitse tiedostot...',
    },
    cancel: 'Peruuta',
    error: {
      tooManyFiles: 'Liian monta tiedostoa.',
      fileIsTooBig: 'Tiedosto on liian suuri.',
      incorrectFileType: 'Väärä tiedostomuoto.',
    },
    uploading: {
      status: {
        connecting: 'Yhdistetään...',
        stalled: 'Pysäytetty',
        processing: 'Käsitellään tiedostoa...',
        held: 'Jonossa',
      },
      remainingTime: {
        prefix: 'aikaa jäljellä: ',
        unknown: 'jäljellä olevaa aikaa ei saatavilla',
      },
      error: {
        serverUnavailable: 'Palvelin ei vastaa',
        unexpectedServerError: 'Palvelinvirhe',
        forbidden: 'Kielletty',
      },
    },
    units: {
      size: ['t', 'kt', 'Mt', 'Gt', 'Tt', 'Pt', 'Et', 'ZB', 'YB'],
      sizeBase: 1000,
    },
  };
  return html`<vaadin-upload .i18n="${i18n}"></vaadin-upload>`;
}

Customization

You can replace the default upload button. For example, if Upload needs a stronger emphasis you can use a primary button.

<vaadin-upload
  max-files="1"
  accept="application/pdf,.pdf"
  @file-reject="${this.fileRejectHandler}"
  @max-files-reached-changed="${(e: UploadMaxFilesReachedChangedEvent) =>
    (this.maxFilesReached = e.detail.value)}"
>
  <vaadin-button slot="add-button" theme="primary" ?disabled="${this.maxFilesReached}">
    Upload PDF...
  </vaadin-button>
</vaadin-upload>

You can also change the drop label’s icon.

<vaadin-upload>
  <iron-icon slot="drop-label-icon" icon="vaadin:folder-open"></iron-icon>
</vaadin-upload>
Note
Use a large drop target

When customizing the Upload component make sure not to make the drop target too small. A large drop target is easier to use and less error prone.

Technical

Listeners

Upload has listeners for the following events:

EventDescription

All Finished

Triggered when Upload has processed all the files in its queue, regardless of whether all the uploads were successful or not

Failed

When the upload is received but the reception is interrupted for some reason

File Rejected

Sent when the file selected for upload doesn’t meet the constraints, for example, file size

Finished

Sent when Upload receives a file regardless of whether the upload was successful or not (to distinguish between the two cases use either Succeeded or Failed listeners)

Progress

Event for tracking upload progress

Started

Triggered when the upload starts

Succeeded

Sent when the upload has been successfully received

Best Practices

Labelling

Choose labels that are informative and instructional. For example, if the user is to upload a single PDF file, it’s better to have the button label say "Upload PDF…​" instead of "Upload File…". The task becomes clearer and improves accessibility for the user, especially if they’re using a screen reader as the button’s label is read aloud when focused.

firstUpdated() {
  if (this.upload?.i18n) {
    this.upload.i18n.addFiles.one = 'Upload PDF...';
    this.upload.i18n.dropFiles.one = 'Drop PDF here';
    this.upload.i18n = { ...this.upload.i18n };
  }
}

render() {
  return html`
    <vaadin-upload
      max-files="1"
      accept="application/pdf,.pdf"
      @file-reject="${this.fileRejectHandler}"
    ></vaadin-upload>
  `;
}

Likewise, if the user is expected to upload a spreadsheet but multiple file formats are accepted, have the button say "Upload Spreadsheet" with helpers to inform the user which formats are accepted.

firstUpdated() {
  if (this.upload?.i18n) {
    this.upload.i18n.addFiles.one = 'Upload Spreadsheet...';
    this.upload.i18n.dropFiles.one = 'Drop spreadsheet here';
    this.upload.i18n = { ...this.upload.i18n };
  }
}

render() {
  return html`
    <h4>Upload spreadsheet</h4>
    <p>
      File size must be less than or equal to ${maxFileSizeInMB} MB.<br />
      Only Excel and CSV files are accepted.
    </p>
    <vaadin-upload
      max-files="1"
      .maxFileSize="${maxFileSizeInBytes}"
      .accept="${acceptedTypes.join(',')}"
      @file-reject="${this.fileRejectHandler}"
    ></vaadin-upload>
  `;
}

Error Messages

Try to provide meaningful feedback and error messages when an exception or error occurs. Avoid technical jargon and instead try to provide solutions/instructions on how to fix the error.

A "Server Unavailable" might suffice for tech savvy users but for some it might not be helpful at all. Your error messages should be written with your users in mind.

render() {
  return html`
        <vaadin-upload
          nodrop
          @upload-response="${this.uploadResponseHandler}"
        ></vaadin-upload>
  `;
}

uploadResponseHandler(event: UploadResponseEvent) {
  const { file, xhr } = event.detail;
  if (xhr.status >= 500) {
    // You can use any information available in the xhr object about the
    // server response for determining what to show in the error message.
    file.error = "File couldn't be uploaded, please try again";
    // Interpret the response as a success so that the custom error message
    // is shown
    (xhr.status as any) = 200;
  }
}

Progress Bar

Component for showing task completion progress.