# File uploads

> Let the customer upload files with a product, such as a logo or a print file, into your store's Shopify Files, with an upload area you design in Webflow.

Status: Ready (Smootify 2.0)  
Plan: Server plan  
Add-on: Product File Upload  
1.x page: https://docs.smootify.io/docs/ecommerce/add-ons/product-file-upload  
URL: https://www.smootify.io/docs/features/file-uploads  
Updated: 2026-10-11

With file uploads, the customer adds files to the product before buying it: a logo to engrave, a print file, a photo for a canvas. The files go straight into your store’s **Shopify Files**, not to another service, and the order line says which files belong to it. You design the upload area with Webflow elements. This page covers `file-uploader`, its parts, the limits and the error messages.

> [!NOTE]
> File uploads need the **server** plan and **Product File Upload** turned on in the Smootify dashboard. They run on the site's licensed domain. On your `.webflow.io` address they run once the site is paid, not during a trial.

## How it fits together

Put a Custom element with the tag `file-uploader` inside the Form of the product’s `smootify-add-to-cart`. It must be inside the Form: that is where it writes the files of the line.

- Give it the custom attribute `name`: the name the files are listed under on the order, such as `Artwork`. Without it, the name is `File`.
- Every part inside is optional. The file picker is added for you: Webflow has no plain file field, so Smootify creates a hidden one.

```html
<file-uploader name="Artwork">
  <div upload="dropzone"><div>Drop your file here, or click to choose it</div></div>
  <div upload="list">
    <div upload="item"><div upload="name">file.png</div><div upload="progress">0%</div><a href="#" upload="remove">Remove</a></div>
  </div>
  <div upload="errors">Error</div>
</file-uploader>
```

The file uploads as soon as the customer picks it. While a file is still uploading, the product cannot be added to the cart: the form waits for it.

## 1. Design the upload area

Add these custom attributes to Webflow elements inside `file-uploader`:

- **`upload="dropzone"`** on a Div block: the area the customer clicks or drops files on. Without it, the whole `file-uploader` is the area.
- **`upload="list"`** on a Div block: it holds one copy of the item per file.
- **`upload="item"`** inside the list: the template of one file. Without it, Smootify makes a plain one.
- **`upload="errors"`** on a Text block: the messages of the files that were refused. It is hidden while there are none.

Inside the item:

- `upload="name"` and `upload="size"`: the file name and size;
- `upload="progress"`: on a Text block, the upload progress as a percentage;
- `upload="preview"` on an Image or a Div block: a preview of an image file, when `data-preview` is on;
- `upload="error"`: why this file failed;
- `upload="remove"` on a link or a Custom element with the tag `button`: removes the file.

What is up to you: the layout, the text of the drop area, which parts the item shows.

**States you can style.** `file-uploader` and each item get these classes. Add them as combo classes:

| Class | On | When |
|---|---|---|
| `is-dragover` | `file-uploader` | a file is dragged over it |
| `is-uploading` | `file-uploader` and the item | a file is uploading: `upload="progress"` counts up, and the form waits for it before adding to the cart |
| `has-files`, `is-done`, `is-image` | `has-files` on `file-uploader`; `is-done` on the item, and `is-image` when it is an image | the file is uploaded: the item shows its name, size and, with `data-preview`, the preview |
| `is-full` | `file-uploader` | the file limit of `data-multiple` is reached |
| `is-invalid` | `file-uploader` | a file was refused: `upload="errors"` shows each refused file and why, such as "page.html: File is of invalid type" |
| `is-error` | the item | the upload failed: its `upload="error"` shows why, such as "Error during upload" |

For a progress bar drawn with CSS, see [CSS](https://www.smootify.io/docs/developers/css).

## 2. Set the limits

These attributes go on `file-uploader`:

| Attribute | What it does | Default |
|---|---|---|
| `data-multiple` | lets the customer upload several files. The value is the most they can upload | one file; 5 with `data-multiple` |
| `data-required` | a file is needed before adding to the cart | off |
| `data-only-images` | accepts images only: JPEG, PNG, WebP, HEIC and GIF | every type except HTML |
| `data-preview` | shows the image in `upload="preview"` | off |
| `max-file-size` | the largest file, with its unit, such as `5MB` or `500KB`: a number alone is in bytes | `20MB` |
| `min-file-size` | the smallest file, such as `50KB` | none |
| `min-resolution` | the fewest pixels of an image, width × height | none |

Without `data-multiple`, a new file replaces the one before. `min-resolution` counts all the pixels: `min-resolution="1000000"` asks for at least one million pixels, such as 1000 × 1000.

`data-required` goes on `file-uploader`, not on a field inside it.

### Shopify’s limits

The files go into your store’s Shopify Files, and Smootify applies Shopify’s own limits, no stricter. No attribute can raise them:

- **Every file:** at most 20 MB. A `max-file-size` above `20MB` does not help: Shopify refuses the file and the upload fails.
- **Types:** every type except HTML: images, PDF, ZIP, AI, PSD, DOCX, CSV and the others. With `data-only-images`, only JPEG, PNG, WebP, HEIC and GIF.
- **Images:** JPEG, PNG, WebP, HEIC and GIF files go in as Shopify images, at most 25 megapixels (width × height, such as 5000 × 5000), with a width and a height no more than 100 times each other. Other image types, such as SVG or TIFF, go in as plain files, without these checks.
- **Videos:** an MP4, MOV or WebM file of up to 20 MB goes in as a plain file, which the merchant opens from the order. It is not a Shopify video.

See [Shopify’s file limits](https://help.shopify.com/en/manual/shopify-admin/productivity-tools/file-uploads).

**Bigger files.** For print files above 20 MB, or videos to keep as videos, file uploads are not the right tool: use an external upload service with the limits you need, such as storage on Cloudflare. If you need one, the team behind Smootify can build it for you on your own Cloudflare account, as an agency project: see [Agency](https://www.smootify.io/agency).

## 3. Error messages

Each refusal has an English message, shown in `upload="errors"`, or in the item’s `upload="error"` when the upload itself fails. To write your own, add `data-error-` followed by the code to `file-uploader`, such as `data-error-max-size="This file is over 5 MB"`.

| Code | When |
|---|---|
| `type` | the file type is not accepted |
| `max-size`, `min-size` | the file is too large, or too small |
| `resolution-low`, `resolution-high` | the image has too few, or too many pixels |
| `ratio` | the image is more than 100 times wider than high, or higher than wide. Default “Image is too narrow or too wide” |
| `max-files` | the customer tries to add more files than `data-multiple` allows |
| `upload` | the upload failed |
| `required` | the customer adds to the cart without a file and `data-required` is on |
| `busy` | the customer adds to the cart while a file is still uploading |

## 4. What reaches the order

The cart line gets the file names under your `name` (`Artwork` in the example). The customer sees them in the cart and at checkout.

The order also holds a link to each file, hidden from the customer. The merchant opens the order and clicks the link to get the file. The link opens the Shopify admin, so it works only for the store’s staff.

**Other places to upload.** The same upload parts work in a `file-input`, which saves the files in a field instead of a cart line: in the cart form ([discounts, notes and progress bars](https://www.smootify.io/docs/build/cart-extras)), in a [form that creates a metaobject](https://www.smootify.io/docs/features/metaobject-creator) and in the [customer’s own data](https://www.smootify.io/docs/build/customer-data). A `configurator-file-input` also counts the files in a price formula: see the [product configurator](https://www.smootify.io/docs/features/product-configurator).

## Before you publish

> [!STUCK]
> - **Nothing happens when the customer picks a file.** The site is not on the server plan, Product File Upload is not turned on, or you are on `.webflow.io` during a trial.
> - **The order has no files.** `file-uploader` is outside the Form of `smootify-add-to-cart`.
> - **A file is refused with no clear reason.** It is an HTML file, which Shopify does not accept, or `data-only-images` is on and the file is not a JPEG, PNG, WebP, HEIC or GIF image. Tell customers which types you accept.
> - **A big file fails to upload.** It is above 20 MB, Shopify's limit, even with a higher `max-file-size`. For bigger files, use an external upload service: see Shopify's limits, above.
> - **The product cannot be added.** A file is still uploading, or `data-required` is on and no file was uploaded.
> - **`data-required` does nothing.** It is on a field inside instead of on `file-uploader`.
> - **The file link seems broken.** It opens the Shopify admin, which needs a staff login.

## Coming from 1.x

- The upload area is no longer the 1.x widget with its own look: it is your markup, with the `upload` parts above. Styles written for the old widget no longer apply, and `max-file-size` is new.
- Every type Shopify accepts can be uploaded, except HTML: 1.x accepted only PNG, JPG, WebP, CSV, PDF and JSON. Images go up to 25 megapixels, and GIF counts as an image.
