Documentation

Documentation versions (currently viewingVaadin 24)

Upload

Upload allows the user to upload files, giving feedback to the user during the upload process.

Upload allows the user to upload files, giving feedback to the user during the upload process. It shows the upload progress and the status of each file. Files can be uploaded by clicking on the Upload button, or by dragging them onto the component.

Open in a
new tab
<!-- Use the target attribute to specify the URL
     that handles the file upload -->
<vaadin-upload
  target="/api/fileupload"></vaadin-upload>

Handling Uploaded Files (Java Only)

The Java Flow Upload component provides an API to handle directly uploaded file data, without having to set up an endpoint or a servlet. It uses a Receiver implementation to write the incoming file data into an OutputStream.

The following default implementations of Receiver are available:

MemoryBuffer

Handles a single file upload at once. Writes the file data into an in-memory buffer. Using MemoryBuffer configures the component so that only a single file can be selected.

MultiFileMemoryBuffer

Handles multiple file uploads at once. Writes the file data into a set of in-memory buffers.

FileBuffer

Handles a single file upload at once. Saves a file on the system, in the current working directory of the Java application. Using FileBuffer configures the component so that only a single file can be selected.

MultiFileBuffer

Handles multiple file uploads at once. For each, it saves a file on the system, in the current working directory of the Java application.

/* Example for MemoryBuffer */
MemoryBuffer memoryBuffer = new MemoryBuffer();
Upload singleFileUpload = new Upload(memoryBuffer);

singleFileUpload.addSucceededListener(event -> {
    // Get information about the uploaded file
    InputStream fileData = memoryBuffer.getInputStream();
    String fileName = event.getFileName();
    long contentLength = event.getContentLength();
    String mimeType = event.getMIMEType();

    // Do something with the file data
    // processFile(fileData, fileName, contentLength, mimeType);
});

/* Example for MultiFileMemoryBuffer */
MultiFileMemoryBuffer multiFileMemoryBuffer = new MultiFileMemoryBuffer();
Upload multiFileUpload = new Upload(multiFileMemoryBuffer);

multiFileUpload.addSucceededListener(event -> {
    // Determine which file was uploaded
    String fileName = event.getFileName();

    // Get input stream specifically for the finished file
    InputStream fileData = multiFileMemoryBuffer
            .getInputStream(fileName);
    long contentLength = event.getContentLength();
    String mimeType = event.getMIMEType();

    // Do something with the file data
    // processFile(fileData, fileName, contentLength, mimeType);
});

For more advanced use cases, you can provide custom implementations for Receiver or MultiFileReceiver. You might do this, for example, to save files into a specific directory, or to upload them to cloud storage.

Handling Upload Requests (Client-Side Only)

When using the Upload web component standalone, you’ll need to set up an upload file handler or endpoint in your backend to handle the file upload request. By default, the Upload component sends a request with the method type POST, the content type multipart/form-data, and the request URL (i.e., the current browser location).

Use the target attribute to specify a different URL that should handle the upload request. It’s also possible to customize other aspects of the request, such as the method or request headers.

Note
Web Component Only
This section is only relevant when using the Upload web component standalone. The Java/Flow component has its own set of APIs to handle file data, directly. It doesn’t require the request to be handled manually.
<vaadin-upload
  method="PUT"
  target="/api/upload-handler"
  headers='{ "X-API-KEY": "7f4306cb-bb25-4064-9475-1254c4eff6e5" }'>
</vaadin-upload>

Drag & Drop

Upload allows the user to drag files onto the component to upload them. Multiple files can be dropped simultaneously. By default, this is enabled on desktop computers and disabled on touch devices. Explicitly setting it to enabled or disabled, though, affects both desktop and mobile devices.

Open in a
new tab
<label for="upload-drop-enabled">Drag and drop enabled</label>
<vaadin-upload id="upload-drop-enabled" .nodrop="${false}"></vaadin-upload>
<label for="upload-drop-disabled">Drag and drop disabled</label>
<vaadin-upload id="upload-drop-disabled" nodrop></vaadin-upload>

Auto-Upload

By default, files are uploaded immediately — or at least they’re added to the queue to be uploaded. 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’s recommended that you change the button label to communicate that uploads don’t start automatically.

Open in a
new tab
@query('vaadin-upload')
private accessor upload!: Upload;

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

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

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

Open in a
new tab
@query('vaadin-upload')
private accessor upload!: Upload;

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

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

Upload Restrictions

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

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

The user should be informed upfront, though, about any file upload restrictions. Limitations on the maximum number of files allowed, file size, and format should all be communicated clearly, to reduce or eliminate exceptions.

File Format

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

Open in a
new tab
<h4>Upload report</h4>
<p>Accepted file formats: PDF (.pdf)</p>
<vaadin-upload
  accept="application/pdf,.pdf"
  max-files="1"
  @file-reject="${(event: UploadFileRejectEvent) => {
    Notification.show(event.detail.error);
  }}"
></vaadin-upload>
Note
Prefer MIME Type
Although MIME types are widely supported, file extensions are only implemented in certain browsers and should be avoided.
Note
File Format Restrictions are Client-Side

File format restrictions set with setAcceptedFileType method are checked only on the client side. They indicate the hints for users as to what file types to upload.

Using this method won’t restrict the uploaded file’s format on the server side. The Upload component doesn’t have an API to restrict uploaded files by file format or content on the server side. If required, it’s the responsibility of the application developer to implement application-specific restrictions on the server side in one or more of the Upload component’s event listeners (e.g., in Upload::addSucceededListener).

File Count

By default, Upload doesn’t limit the number of files that can be uploaded. However, you can set a file count limit. If you set the maximum to one, the native file browser prevents multiple files from being selected.

Note
Java Flow-Specific
When using a Receiver that doesn’t implement the MultiFileReceiver interface — such as MemoryBuffer or FileBuffer — the Upload component limits the number of files to one. This is because these receiver implementations only support handling a single file at once.
Open in a
new tab
protected override render() {
  const maxFiles = 3;
  return html`
    <h4>Upload files</h4>
    <p>Maximum of ${maxFiles} files allowed</p>
    <vaadin-upload
      .maxFiles="${maxFiles}"
      @file-reject="${(event: UploadFileRejectEvent) => {
        Notification.show(event.detail.error);
      }}"
    ></vaadin-upload>
  `;
}

File Size

Upload allows you to limit the file size by setting a maximum amount in bytes. By default, though, there is no limit.

Open in a
new tab
protected override render() {
  const maxFileSizeInMB = 10;
  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="${(event: UploadFileRejectEvent) => {
        Notification.show(event.detail.error);
      }}"
    ></vaadin-upload>
  `;
}
Note
Revalidate Size Limit on 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. This button is the only available action during and after a successful upload.

Open in a
new tab
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '@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 override createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

  protected override render() {
    return html`<vaadin-upload .files="${createFakeFiles()}"></vaadin-upload>`;
  }
}
Note
Remove the File
The Clear/Remove button doesn’t remove a successfully uploaded file from the server file system or database. It’s only removed from the file list. Remember to remove the file from the backend.

If an error or exception occurs, Upload displays a Retry button for the user to be able to try to upload the file again.

Open in a
new tab
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '@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 override createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

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

When a file is queued (i.e., auto-upload is disabled), there’s a Start Button that the user must press to begin the upload process.

Open in a
new tab
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '@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 override createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

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

Internationalization (i18n)

All labels and messages in Upload are configurable. For a complete list of them, see the API documentation (Java and Web component).

Open in a
new tab
protected override render() {
  const i18n: UploadI18n = {
    dropFiles: {
      one: 'Raahaa tiedosto tähän',
      many: 'Raahaa tiedostot tähän',
    },
    addFiles: {
      one: 'Valitse tiedosto...',
      many: 'Valitse tiedostot...',
    },
    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. You might do this if Upload needs a stronger emphasis. If so, you can use a primary button.

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

You can also customize the drop label, as well as the icon.

Open in a
new tab
<vaadin-upload>
  <vaadin-icon slot="drop-label-icon" icon="vaadin:cloud-upload-o"></vaadin-icon>
  <span slot="drop-label">
    Files will be uploaded to our cloud. See our
    <a href="https://vaadin.com/privacy-policy" target="_blank">privacy policy</a>
  </span>
</vaadin-upload>
Tip
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.

Listeners

Upload has listeners for the following events:

All Finished

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

Failed

Occurs 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 (e.g., file size limit).

Finished

Sent when Upload receives a file, regardless of whether the upload was successful. To distinguish results, use instead either Succeeded or Failed listeners.

Progress

Event for tracking upload progress.

Started

Triggered when the upload starts.

Succeeded

Sent when the upload has been received successfully.

Best Practices

With regards to developing with Upload, this section provides some suggestions on how to label buttons and how to construct error messages for better user experiences.

Labeling

Choose labels that are informative and instructive. 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.

Open in a
new tab
protected override firstUpdated() {
  this.upload.i18n.addFiles.one = 'Upload PDF...';
  this.upload.i18n.dropFiles.one = 'Drop PDF here';
  this.upload.i18n.error.incorrectFileType =
    'The provided file does not have the correct format (PDF document).';
  this.upload.i18n = { ...this.upload.i18n };
}

protected override 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, label the button, "Upload Spreadsheet". Include helpers to inform the user which formats are accepted.

Open in a
new tab
protected override firstUpdated() {
  this.upload.i18n.addFiles.one = 'Upload Spreadsheet...';
  this.upload.i18n.dropFiles.one = 'Drop spreadsheet here';
  this.upload.i18n.error.incorrectFileType =
    'Provide the file in one of the supported formats (.xls, .xlsx, .csv).';
  this.upload.i18n = { ...this.upload.i18n };
}

protected override 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. Instead, try to provide solutions and instructions on how to fix the error.

A "Server Unavailable" message might suffice for tech-savvy users, but for some it might be lacking, unhelpful, and frustrating. Error messages should be written with your users in mind.

Open in a
new tab
protected override firstUpdated() {
  this.uploadRecommended.i18n.uploading.error.unexpectedServerError =
    "File couldn't be uploaded, try again later";
  this.uploadRecommended.i18n = { ...this.uploadRecommended.i18n };
}

protected override render() {
  return html`
        <vaadin-upload
          id="upload-recommended"
          nodrop
        ></vaadin-upload>
  `;
}

Progress Bar

Component for showing task completion progress.

7426DF11-9CAE-4B52-B1BF-28F3318F58AE