> Markdown version of [Upload](https://vaadin.com/docs/latest/components/upload). Section index: [llms.txt](https://vaadin.com/docs/latest/components/llms.txt)

# Upload

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.

**Lit** — `upload-basic.ts`

```html
<!-- Use the target attribute to specify the URL
     that handles the file upload -->
<vaadin-upload
  target="/api/fileupload"></vaadin-upload>
```

**Flow** — `UploadBasic.java`

```java
InMemoryUploadHandler inMemoryHandler = UploadHandler
        .inMemory((metadata, data) -> {
            // Get other information about the file.
            String fileName = metadata.fileName();
            String mimeType = metadata.contentType();
            long contentLength = metadata.contentLength();

            // Do something with the file data...
            // processFile(data, fileName);
        });
Upload upload = new Upload(inMemoryHandler);
```

**React** — `upload-basic.tsx`

```tsx
{/* Use the target attribute to specify the URL that handles the file upload */}
<Upload
  target="/api/fileupload"
  files={files.value}
  // ... other properties
/>
```

## <a id="drag-drop"></a>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.

**Lit** — `upload-drag-and-drop.ts`

```html
<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>
```

**Flow** — `UploadDragAndDrop.java`

```java
Upload dropEnabledUpload = new Upload(inMemoryHandler1);
dropEnabledUpload.setDropAllowed(true);

Upload dropDisabledUpload = new Upload(inMemoryHandler2);
dropDisabledUpload.setDropAllowed(false);
```

**React** — `upload-drag-and-drop.tsx`

```tsx
<Upload id="upload-drop-disabled" nodrop />
```

Upload supports uploading folders via drag & drop. This uploads all files from the folder and its sub-folders recursively. The folder structure is flattened and the names of the uploaded files don’t indicate in which folder they were in. Uploading folders is currently not supported through the native file picker.

## <a id="auto-upload"></a>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. Change the button label, though, to indicate that uploads don’t start automatically.

**Lit** — `upload-auto-upload-disabled.ts`

```typescript
private uploadI18n = {
  addFiles: {
    many: 'Select Files...',
  },
};

protected override render() {
  return html`
    <vaadin-upload
      no-auto
      .i18n="${this.uploadI18n}"
    ></vaadin-upload>
  `;
}
```

**Flow** — `UploadAutoUploadDisabled.java`

```java
Upload upload = new Upload(inMemoryHandler);
upload.setAutoUpload(false);

UploadExamplesI18N i18n = new UploadExamplesI18N();
i18n.getAddFiles().setMany("Select Files...");
upload.setI18n(i18n);
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-auto-upload-disabled.tsx`

```tsx
<Upload noAuto files={files.value} i18n={uploadI18n.value} />
```

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.

**Lit** — `upload-all-files.ts`

```typescript
@query('vaadin-upload')
private upload!: Upload;

protected override render() {
  return html`
    <vaadin-upload
      no-auto
      .i18n="${this.uploadI18n}"
    ></vaadin-upload>
    <vaadin-button
      theme="primary"
      style="margin-top: var(--vaadin-gap-s)"
      @click="${this.uploadFiles}"
    >
      Upload All Files
    </vaadin-button>
  `;
}

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

**Flow** — `UploadAllFiles.java`

```java
Upload upload = new Upload(inMemoryHandler);
upload.setAutoUpload(false);

Button uploadAllButton = new Button("Upload All Files");
uploadAllButton.addThemeVariants(ButtonVariant.PRIMARY);
uploadAllButton.getStyle().set("margin-top", "var(--vaadin-gap-s)");
uploadAllButton.addClickListener(event -> {
    // No explicit Flow API for this at the moment
    upload.getElement().callJsFunction("uploadFiles");
});
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-all-files.tsx`

```tsx
<Upload noAuto ref={uploadRef} files={files.value} i18n={uploadI18n.value} />

<Button
  theme="primary"
  style={{ marginTop: 'var(--vaadin-gap-s)' }}
  onClick={() => uploadRef.current?.uploadFiles()}
>
  Upload All Files
</Button>
```

## <a id="upload-restrictions"></a>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](#listeners) to catch those exceptions and, for example, a [Notification](https://vaadin.com/docs/latest/components/notification.md) 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.

### <a id="file-format"></a>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"`).

**Lit** — `upload-file-format.ts`

```html
<h4>Upload report</h4>
<p>Accepted file formats: PDF (.pdf)</p>
<vaadin-upload
  accept="application/pdf,.pdf"
  max-files="1"
  .i18n="${this.uploadI18n}"
  @file-reject="${(event: UploadFileRejectEvent) => {
    Notification.show(event.detail.error, { position: 'middle', theme: 'error' });
  }}"
></vaadin-upload>
```

**Flow** — `UploadFileFormat.java`

```java
Upload upload = new Upload(inMemoryUploadHandler);
upload.setAcceptedFileTypes("application/pdf", ".pdf");

upload.addFileRejectedListener(event -> {
    String errorMessage = event.getErrorMessage();

    Notification notification = Notification.show(errorMessage, 5000,
            Notification.Position.MIDDLE);
    notification.addThemeVariants(NotificationVariant.ERROR);
});
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-file-format.tsx`

```tsx
<h4>Upload report</h4>
<p style={{ color: 'var(--vaadin-text-color-secondary)' }}>
  Accepted file formats: PDF (.pdf)
</p>
<Upload
  accept="application/pdf,.pdf"
  maxFiles={1}
  i18n={uploadI18n.value}
  onFileReject={(event) => {
    Notification.show(event.detail.error, { position: 'middle', theme: 'error' });
  }}
/>
```

> **Note: Prefer MIME Type**
>
> Although [MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) are widely supported, file extensions are only implemented [in certain browsers](https://caniuse.com/input-file-accept) 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`).

### <a id="file-count"></a>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: Potential Performance Degradation**
>
> The Upload component has no built-in limit on the number of files that can be uploaded simultaneously. However, performance may degrade after a few hundred files, and browser-specific XHR limitations can cause uploads to fail when attempting to upload more than 2000 files at once. To ensure reliable operation and good user experience, set a file count limit (for example, around 100).

> **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.

**Lit** — `upload-file-count.ts`

```typescript
protected override render() {
  const maxFiles = 3;
  return html`
    <h4>Upload files</h4>
    <p>Maximum of ${maxFiles} files allowed</p>
    <vaadin-upload
      .maxFiles="${maxFiles}"
      .i18n="${this.uploadI18n}"
      @file-reject="${(event: UploadFileRejectEvent) => {
        Notification.show(event.detail.error, { position: 'middle', theme: 'error' });
      }}"
    ></vaadin-upload>
  `;
}
```

**Flow** — `UploadFileCount.java`

```java
Upload upload = new Upload(inMemoryUploadHandler);
upload.setMaxFiles(3);

upload.addFileRejectedListener(event -> {
    String errorMessage = event.getErrorMessage();

    Notification notification = Notification.show(errorMessage, 5000,
            Notification.Position.MIDDLE);
    notification.addThemeVariants(NotificationVariant.ERROR);
});
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-file-count.tsx`

```tsx
const maxFiles = 3;

const uploadI18n = useSignal({
  error: {
    tooManyFiles: 'You may only upload a maximum of three files at once.',
  },
});

return (
  <>
    <h4>Upload files</h4>
    <p style={{ color: 'var(--vaadin-text-color-secondary)' }}>
      Maximum of {maxFiles} files allowed
    </p>
    <Upload
      maxFiles={maxFiles}
      i18n={uploadI18n.value}
      onFileReject={(event) => {
        Notification.show(event.detail.error, { position: 'middle', theme: 'error' });
      }}
    />
  </>
);
```

### <a id="file-size"></a>File Size

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

**Lit** — `upload-file-size.ts`

```typescript
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}"
      .i18n="${this.uploadI18n}"
      @file-reject="${(event: UploadFileRejectEvent) => {
        Notification.show(event.detail.error, { position: 'middle', theme: 'error' });
      }}"
    ></vaadin-upload>
  `;
}
```

**Flow** — `UploadFileSize.java`

```java
Upload upload = new Upload(inMemoryUploadHandler);

int maxFileSizeInBytes = 10 * 1024 * 1024; // 10MB
upload.setMaxFileSize(maxFileSizeInBytes);

upload.addFileRejectedListener(event -> {
    String errorMessage = event.getErrorMessage();

    Notification notification = Notification.show(errorMessage, 5000,
            Notification.Position.MIDDLE);
    notification.addThemeVariants(NotificationVariant.ERROR);
});
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-file-size.tsx`

```tsx
const maxFileSizeInMB = 10;
const maxFileSizeInBytes = maxFileSizeInMB * 1024 * 1024;

const uploadI18n = useSignal({
  error: {
    fileIsTooBig: 'The file exceeds the maximum allowed size of 10MB.',
  },
});

return (
  <>
    <h4>Upload file</h4>
    <p style={{ color: 'var(--vaadin-text-color-secondary)' }}>
      Maximum file size: {maxFileSizeInMB} MB
    </p>
    <Upload
      maxFiles={1}
      maxFileSize={maxFileSizeInBytes}
      i18n={uploadI18n.value}
      onFileReject={(event) => {
        Notification.show(event.detail.error, { position: 'middle', theme: 'error' });
      }}
    />
  </>
);
```

> **Note: Revalidate Size Limit on Server**
>
> This constraint is set on the client and is checked before contacting the server.
>
> When using Vaadin Flow with Spring Boot, file size limits are also affected by Spring’s multipart configuration.
>
> By default, Spring Boot applies restrictive limits that may cause uploads to fail — typically for files larger than 1MB. You can increase these limits in your `application.properties` or `application.yml` file:
>
> ```properties
> spring.servlet.multipart.max-file-size=50MB
> spring.servlet.multipart.max-request-size=100MB
> ```
>
> Additionally, if you’re using an `UploadHandler` or `Receiver`, you may need to disable Spring’s multipart resolver entirely to allow the Upload component to handle the stream directly:
>
> ```properties
> spring.servlet.multipart.enabled=false
> ```
>
> By default, Spring intercepts multipart requests and caches the file content before the Upload component receives it. This behavior can prevent upload event listeners or stream receivers from working correctly.

## <a id="file-actions"></a>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.

`upload-clear-button.ts`

```typescript
import '@vaadin/upload';
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { applyTheme } from 'Frontend/demo/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();
    applyTheme(root);
    return root;
  }

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

**React** — `upload-clear-button.tsx`

```tsx
import React from 'react';
import { useComputed } from '@vaadin/hilla-react-signals';
import { Upload } from '@vaadin/react-components/Upload.js';
import { createFakeUploadFiles } from './upload-demo-helpers';

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

function Example() {
  const files = useComputed(createFakeFiles);
  return <Upload files={files.value} />;
}
```

> **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.

`upload-retry-button.ts`

```typescript
import '@vaadin/upload';
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { applyTheme } from 'Frontend/demo/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();
    applyTheme(root);
    return root;
  }

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

**React** — `upload-retry-button.tsx`

```tsx
<Upload files={files} />
```

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.

`upload-start-button.ts`

```typescript
import '@vaadin/upload';
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { applyTheme } from 'Frontend/demo/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();
    applyTheme(root);
    return root;
  }

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

**React** — `upload-start-button.tsx`

```tsx
<Upload files={files.value} />
```

## <a id="internationalization-i18n"></a>Internationalization (i18n)

All labels and messages in Upload are configurable. For a complete list of them, see the API documentation ([Java](https://vaadin.com/api/platform/25.3.1/com/vaadin/flow/component/upload/UploadI18N.html) and [Web component](https://cdn.vaadin.com/vaadin-web-components/25.3.2/elements/vaadin-upload#property-i18n)).

**Lit** — `upload-internationalization.ts`

```typescript
private 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,
  },
};

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

**Flow** — `UploadInternationalization.java`

```java
Upload upload = new Upload(inMemoryUploadHandler);

// See the separate UploadFinnishI18N class / file
// in this example for the I18N configuration
UploadFinnishI18N i18N = new UploadFinnishI18N();
upload.setI18n(i18N);
```

**Flow** — `UploadFinnishI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts. Make sure
 * that you have set all translations when writing a custom I18N config.
 */
public class UploadFinnishI18N extends UploadI18N {
    public UploadFinnishI18N() {
        setDropFiles(new DropFiles().setOne("Raahaa tiedosto tähän")
                .setMany("Raahaa tiedostot tähän"));
        setAddFiles(new AddFiles().setOne("Valitse tiedosto...")
                .setMany("Valitse tiedostot..."));
        setError(new Error().setTooManyFiles("Liian monta tiedostoa.")
                .setFileIsTooBig("Tiedosto on liian suuri.")
                .setIncorrectFileType("Väärä tiedostomuoto."));
        setUploading(new Uploading().setStatus(new Uploading.Status()
                .setConnecting("Yhdistetään...").setStalled("Pysäytetty")
                .setProcessing("Käsitellään tiedostoa...").setHeld("Jonossa"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("aikaa jäljellä: ")
                        .setUnknown("jäljellä olevaa aikaa ei saatavilla"))
                .setError(new Uploading.Error()
                        .setServerUnavailable("Palvelin ei vastaa")
                        .setUnexpectedServerError("Palvelinvirhe")
                        .setForbidden("Kielletty")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-internationalization.tsx`

```tsx
const uploadI18n = useSignal({
  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 <Upload i18n={uploadI18n.value} />;
```

## <a id="customization"></a>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.

**Lit** — `upload-button-theme-variant.ts`

```html
<vaadin-upload
  max-files="1"
  accept="application/pdf,.pdf"
  .i18n="${this.uploadI18n}"
  @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>
```

**Flow** — `UploadButtonThemeVariant.java`

```java
Upload upload = new Upload(inMemoryUploadHandler);
upload.setMaxFiles(1);
upload.setAcceptedFileTypes("application/pdf", ".pdf");

Button uploadButton = new Button("Upload PDF...");
uploadButton.addThemeVariants(ButtonVariant.PRIMARY);

upload.setUploadButton(uploadButton);

// Disable the upload button after the file is selected
// Re-enable the upload button after the file is cleared
upload.getElement()
        .addEventListener("max-files-reached-changed", event -> {
            boolean maxFilesReached = event.getEventData()
                    .get("event.detail.value").asBoolean();
            uploadButton.setEnabled(!maxFilesReached);
        }).addEventData("event.detail.value");
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-button-theme-variant.tsx`

```tsx
<Upload
  maxFiles={1}
  accept="application/pdf,.pdf"
  onFileReject={fileRejectHandler}
  onMaxFilesReachedChanged={maxFilesReachedChangedHandler}
>
  <Button slot="add-button" theme="primary" disabled={maxFilesReached.current}>
    Upload PDF...
  </Button>
</Upload>
```

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

**Lit** — `upload-drop-label.ts`

```html
<vaadin-upload>
  <vaadin-icon slot="drop-label-icon" icon="vaadin:cloud-upload"></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>
```

**Flow** — `UploadDropLabel.java`

```java
Upload upload = new Upload(inMemoryHandler);

Span dropLabel = createDropLabel();
Icon dropIcon = VaadinIcon.CLOUD_UPLOAD.create();

upload.setDropLabel(dropLabel);
upload.setDropLabelIcon(dropIcon);
```

**React** — `upload-drop-label.tsx`

```tsx
<Upload>
  <Icon slot="drop-label-icon" icon="vaadin:cloud-upload" />
  <span slot="drop-label">
    Files will be uploaded to our cloud. See our&nbsp;
    <a href="https://vaadin.com/privacy-policy" target="_blank">
      privacy policy
    </a>
  </span>
</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.

## <a id="listeners"></a>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.

- *File Rejected*

  Sent when the file selected for upload doesn’t meet the constraints (e.g., file size limit).

For the following upload events TransferProgressListener should be used, see [progress listener](https://vaadin.com/docs/latest/components/upload/file-handling.md#add-progress-listener)

- *Started*

  TransferProgressListener::onStart

- *Finished* and *Succeeded*

  TransferProgressListener::onComplete

- *Progress*

  TransferProgressListener::onProgress

- *Failed*

  TransferProgressListener::onError

## <a id="best-practices"></a>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.

### <a id="labeling"></a>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.

**Lit** — `upload-labelling.ts`

```typescript
private uploadI18n = {
  addFiles: { one: 'Upload Report...' },
  dropFiles: { one: 'Drop report here' },
  error: {
    incorrectFileType: 'The provided file does not have the correct format (PDF document).',
  },
};

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

**Flow** — `UploadLabelling.java`

```java
Upload upload = new Upload(inMemoryUploadHandler);
upload.setAcceptedFileTypes("application/pdf", ".pdf");

UploadExamplesI18N i18n = new UploadExamplesI18N();
i18n.getAddFiles().setOne("Upload PDF...");
i18n.getDropFiles().setOne("Drop PDF here");
i18n.getError().setIncorrectFileType(
        "The provided file does not have the correct format (PDF document).");
upload.setI18n(i18n);
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-labelling.tsx`

```tsx
const uploadI18n = useSignal({
  addFiles: {
    one: 'Upload PDF...',
  },
  dropFiles: {
    one: 'Drop PDF here',
  },
  error: {
    incorrectFileType: 'The provided file does not have the correct format (PDF document).',
  },
});

return (
  <Upload
    maxFiles={1}
    accept="application/pdf,.pdf"
    i18n={uploadI18n.value}
    onFileReject={fileRejectHandler}
  />
);
```

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.

**Lit** — `upload-helper.ts`

```typescript
private uploadI18n = {
  addFiles: { one: 'Upload Spreadsheet...' },
  dropFiles: { one: 'Drop spreadsheet here' },
  error: {
    incorrectFileType: 'Provide the file in one of the supported formats (.xls, .xlsx, .csv).',
  },
};

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(',')}"
      .i18n="${this.uploadI18n}"
      @file-reject="${this.fileRejectHandler}"
    ></vaadin-upload>
  `;
}
```

**Flow** — `UploadHelper.java`

```java
H4 title = new H4("Upload spreadsheet");
Paragraph hint = new Paragraph(
        "File size must be less than or equal to 1 MB. Only Excel and CSV files are accepted.");

Upload upload = new Upload(inMemoryUploadHandler);
upload.setAcceptedFileTypes(
        // Microsoft Excel (.xls)
        "application/vnd.ms-excel", ".xls",
        // Microsoft Excel (OpenXML, .xlsx)
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        ".xlsx",
        // Comma-separated values (.csv)
        "text/csv", ".csv");

int maxFileSizeInBytes = 1 * 1024 * 1024; // 1MB
upload.setMaxFileSize(maxFileSizeInBytes);

UploadExamplesI18N i18n = new UploadExamplesI18N();
i18n.getAddFiles().setOne("Upload Spreadsheet...");
i18n.getDropFiles().setOne("Drop spreadsheet here");
i18n.getError().setIncorrectFileType(
        "Provide the file in one of the supported formats (.xls, .xlsx, .csv).");
upload.setI18n(i18n);

add(title, hint, upload);
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-helper.tsx`

```tsx
const uploadI18n = useSignal({
  addFiles: {
    one: 'Upload Spreadsheet...',
  },
  dropFiles: {
    one: 'Drop spreadsheet here',
  },
  error: {
    incorrectFileType: 'Provide the file in one of the supported formats (.xls, .xlsx, .csv).',
  },
});

return (
  <>
    <h4>Upload spreadsheet</h4>
    <p style={{ color: 'var(--vaadin-text-color-secondary)' }}>
      File size must be less than or equal to {maxFileSizeInMB} MB.
      <br />
      Only Excel and CSV files are accepted.
    </p>
    <Upload
      maxFiles={1}
      maxFileSize={maxFileSizeInBytes}
      accept={acceptedTypes.join(',')}
      i18n={uploadI18n.value}
      onFileReject={(event) => {
        Notification.show(event.detail.error, { position: 'middle', theme: 'error' });
      }}
    />
  </>
);
```

### <a id="error-messages"></a>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.

**Lit** — `upload-error-messages.ts`

```typescript
private recommendedI18n = {
  uploading: {
    error: {
      unexpectedServerError: "File couldn't be uploaded, try again later",
    },
  },
};

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

**Flow** — `UploadErrorMessages.java`

```java
Upload upload = new Upload(inMemoryUploadHandler);
upload.setDropAllowed(false);

UploadExamplesI18N i18N = new UploadExamplesI18N();
i18N.getUploading().getError().setUnexpectedServerError(
        "File couldn't be uploaded, please try again later");
upload.setI18n(i18N);
```

**Flow** — `UploadExamplesI18N.java`

```java
package com.vaadin.demo.component.upload;

import com.vaadin.flow.component.upload.UploadI18N;

import java.util.Arrays;

/**
 * Provides a default I18N configuration for the Upload examples
 *
 * At the moment the Upload component requires a fully configured I18N instance,
 * even for use-cases where you only want to change individual texts.
 *
 * This I18N configuration is an adaption of the web components I18N defaults
 * and can be used as a basis for customizing individual texts.
 */
public class UploadExamplesI18N extends UploadI18N {
    public UploadExamplesI18N() {
        setDropFiles(new DropFiles().setOne("Drop file here")
                .setMany("Drop files here"));
        setAddFiles(new AddFiles().setOne("Upload File...")
                .setMany("Upload Files..."));
        setError(new Error().setTooManyFiles("Too Many Files.")
                .setFileIsTooBig("File is Too Big.")
                .setIncorrectFileType("Incorrect File Type."));
        setUploading(new Uploading()
                .setStatus(new Uploading.Status().setConnecting("Connecting...")
                        .setStalled("Stalled")
                        .setProcessing("Processing File...").setHeld("Queued"))
                .setRemainingTime(new Uploading.RemainingTime()
                        .setPrefix("remaining time: ")
                        .setUnknown("unknown remaining time"))
                .setError(new Uploading.Error()
                        .setServerUnavailable(
                                "Upload failed, please try again later")
                        .setUnexpectedServerError(
                                "Upload failed due to server error")
                        .setForbidden("Upload forbidden")));
        setUnits(new Units().setSize(Arrays.asList("B", "kB", "MB", "GB", "TB",
                "PB", "EB", "ZB", "YB")));
    }
}
```

**React** — `upload-error-messages.tsx`

```tsx
const recommendedI18n = useSignal({
  uploading: {
    error: {
      unexpectedServerError: "File couldn't be uploaded, try again later",
    },
  },
});

return (
  <FormLayout responsiveSteps={layoutSteps}>
    <div>
      <strong>Caution</strong>
      <Upload ref={uploadCaution} nodrop files={filesA.value} i18n={cautionI18n.value} />
    </div>

    <div>
      <strong>Recommended</strong>
      <Upload ref={uploadRecommended} nodrop files={filesB.value} i18n={recommendedI18n.value} />
    </div>
  </FormLayout>
);
```

### <a id="component-state"></a>Component State

To conserve resources, the files themselves aren’t part of the component’s server-side state. This has significant implications for component behavior. Removing the Upload component from the layout means that the files in the browser memory are lost. The same applies when reloading the page. Read more about [file handling](https://vaadin.com/docs/latest/components/upload/file-handling.md).

The user interface, including the Upload component, should take this into account. For example, if you expect that the user will refresh the page, you can display a message to the user that files needs to be selected again, if this wasn’t the first time the page was rendered.

## <a id="related-components"></a>Related Components

| Component                                                                            | Usage Recommendation                                                                |
| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| [Modular Upload](https://vaadin.com/docs/latest/components/upload/modular-upload.md) | Independent upload components for flexible layouts and embedded upload experiences. |
| [Progress Bar](https://vaadin.com/docs/latest/components/progress-bar.md)            | Component for showing task completion progress.                                     |

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