---
title: Migrate DropZone from Polaris React
description: >-
  Replace Polaris React DropZone with s-drop-zone and reconnect file validation,
  upload progress, errors, retry, and removal.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/drop-zone
  md: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/drop-zone.md
api_name: app-home
---

# Migrate Drop​Zone from Polaris React

Replace Polaris React `DropZone` with [`s-drop-zone`](https://shopify.dev/docs/api/app-home/web-components/forms/drop-zone). The component selects files and validates accepted file types. Your app still owns file-size and business validation, upload requests, progress, persistence, retry, and removal.

***

## Migrate file selection and upload

The following destination preserves image-only, multiple-file selection. Reconnect validation and upload handling through the events described below.

## Migrating an image upload

##### Polaris web components

```html
<s-drop-zone accept="image/*" label="Upload images" multiple></s-drop-zone>
```

##### Polaris React

```tsx
import {DropZone} from '@shopify/polaris';

export function ProductImageUpload({uploadImages, error}) {
  return (
    <DropZone
      label="Upload images"
      accept="image/*"
      allowMultiple
      variableHeight
      error={Boolean(error)}
      onDrop={(_files, acceptedFiles) => uploadImages(acceptedFiles)}
    >
      <DropZone.FileUpload
        actionTitle="Add files"
        actionHint="Accepts image files"
      />
    </DropZone>
  );
}
```

***

## Map file-selection properties

| Polaris React | Polaris web components | Migration notes |
| - | - | - |
| `label` | `label` | Keep the upload purpose visible. |
| `labelHidden` | `labelAccessibilityVisibility="exclusive"` | Keep an accessible field label. |
| `accept` | `accept` | Keep file extensions or MIME types aligned with server validation. |
| `allowMultiple` | `multiple` | Omit it when exactly one file is allowed. |
| `disabled` | `disabled` | Disable new selection while an upload is being committed when concurrent changes aren't safe. |
| `error` boolean and custom child message | `error` string | Put the recovery message on the field. |
| `onDrop(files, accepted, rejected)` | `onChange(event)` and `onDropRejected(event)` | Read selected files from `event.currentTarget.files`. Handle type rejection separately. |
| `customValidator` | App validation in the selection handler | Validate size, count, dimensions, and domain rules before upload. Repeat validation on the server. |
| `dropOnPage` | Remove | Keep the drop target local to the upload task so unrelated page drops aren't intercepted. |
| `openFileDialog` | Remove | Let the merchant activate the field rather than controlling the browser file dialog from render state. |

`accept` doesn't validate file size, image dimensions, content safety, or whether a file matches its extension. Treat browser validation as early feedback and enforce every requirement again on the server.

***

## Rebuild the upload lifecycle

Keep each selected file in app state with a stable client ID and explicit status:

1. Validate the selected files and show per-file or field-level rejection messages.
2. Start the upload and prevent accidental duplicate submission.
3. Display progress when the upload transport reports measurable progress; otherwise show an indeterminate pending state.
4. On success, store the server resource ID and render the uploaded-file state.
5. On failure, preserve the file name and a retry or remove action.
6. When a merchant removes an uploaded file, update both backend state and the rendered list.

Don't clear the selected files before the server confirms success. A failed upload with no visible file or retry path forces the merchant to repeat selection.

***

## Test the migration

* Select files through browse and drag-and-drop interactions.
* Test one and multiple files, accepted and rejected types, size limits, and count limits.
* Run upload success, failure, cancellation, retry, duplicate submission, and removal paths.
* Confirm server validation rejects files that bypass browser checks.
* Verify disabled and pending states don't strand keyboard focus.
* Verify labels, rejection messages, file names, and retry actions are announced.

***

## Remove Polaris React

After every call site is migrated, remove `DropZone`, `DropZone.FileUpload`, Polaris drag-state helpers, and callback adapters used only by them. Remove `@shopify/polaris` only after no other route in scope imports it.

***

## Related guidance

* [Drop zone component](https://shopify.dev/docs/api/app-home/web-components/forms/drop-zone)
* [Migrate Thumbnail from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/thumbnail)
* [Migrate Spinner from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/spinner)

***
