Documentation

Flowdrive docs

Upload Widget

Receive files from forms on your site, on Webflow or any framework

Last updated: October 19, 2026

Upload Widget

Add a file upload field to any form on your site. Visitors pick a file, it uploads to Flowdrive, and the form gets the file's link.

The quickest way to set it up is the Upload Widget page in your dashboard: it walks you through adding your site, installing the widget, checking your page and sending a test file.

1. Add your site

Uploads only work on the sites listed under Upload Widget → Your sites. Until a site is listed, the widget shows on your page but every upload is refused.

On Webflow, there's nothing to add here. When you turn on uploads for a site in the Flowdrive app, Flowdrive reads the site's webflow.io address and custom domains from Webflow and lists them under Webflow sites, marked Added by the Webflow app. Webflow addresses can't be typed in by hand.

Any other site: add it under Other sites (not built on Webflow). Subdomains are included, so acme.com also covers www.acme.com. Add your staging domain too if it's on a different address (for example acme-staging.netlify.app).

2. Install the widget

Webflow

In the Webflow Designer, open the Flowdrive app, click Upload widget and Enable uploads, then add the field to your form and publish. On Webflow, use the app rather than the script below: the app adds the script to your site through Webflow's Custom Code. The full guide is Flowdrive for Webflow.

Any other site (not built on Webflow)

Add the script once, in your page's <head> or before </body>. Your upload key is on the Upload Widget page.

html
<script data-fd-id="YOUR_UPLOAD_KEY" src="https://files.tryflowdrive.com/widget/1.1.15/index.min.js" integrity="sha256-C+zaPFmxVCrCymax8LpmG3jlN7TIgqkeqh47eIWcN24=" crossorigin="anonymous"></script>

Then add the upload field inside your form:

html
<form>
  <div fd-upload="default" fd-input="file_url"></div>
</form>

When a file finishes uploading, its link is put in a form field called file_url, so it's sent with the rest of the form.

If your site uses a Content Security Policy

A Content-Security-Policy header makes the browser block anything it doesn't list. If your site sends one, add these sources, or the widget won't load, its styles and image previews won't show, or uploads will fail:

DirectiveAddWhy
script-srchttps://files.tryflowdrive.comThe widget script
style-srchttps://files.tryflowdrive.comThe widget's stylesheet
img-srcblob: data: https:Previews of chosen images, and the uploaded files
connect-srchttps://api.tryflowdrive.com https://files.tryflowdrive.comUploads, and signed links for secured files

Webflow sites don't restrict scripts, styles or images unless you add your own policy, so they usually need nothing here.

Attributes reference

Every attribute the upload widget reads, in one place. Attributes go on the elements of your page; values are plain text.

Adding an attribute in Webflow

To change an upload field the Flowdrive app added, beyond the options in the app. The app adds a custom field, so the attributes marked Custom field below, and unmarked ones such as fd-folder, work on it. Those marked Built-in field don't. See Flowdrive for Webflow.

  1. In the Designer, select the upload field the Flowdrive app added.
  2. Open the Settings panel (the ⚙ tab, or press D) and find Custom attributes.
  3. Click +, type the attribute in Name (for example fd-max-size) and its value in Value (for example 10MB). For an attribute with no value, leave Value empty.
  4. Publish. Custom code doesn't run inside the Designer, so test on your published or staging site.

On any other site, write the attribute straight into your HTML:

html
<div fd-upload="default" fd-upload-text="Attach your CV"></div>

On the script

AttributeWhat it does
data-fd-id="YOUR_UPLOAD_KEY"Identifies your workspace. Your upload key is on the Upload Widget page. It's upload-only, so it's safe in a public page.

On the upload field

The element with fd-upload. Some attributes only work on the built-in field (default), others only on your own design (custom).

AttributeWhat it does
fd-upload="default"Turns the element into an upload field. default is the built-in field; custom uses your own button and preview (see Custom design).
fd-input="file_url"Where the file's link goes. A name (file_url) adds a hidden field with that name to the form; a selector (#cv, .cv, [name=cv]) uses a field you already have. That field must be a text, URL, search or hidden input, or a textarea. The widget won't write to password, email, phone or number inputs or dropdowns; to fields whose autocomplete marks them as a password, username, one-time-code or card field; to fields whose name or id contains password, passwd, csrf or xsrf, or a word such as pwd, otp, token, nonce, secret, cvc, cvv or "card number"; to reCAPTCHA, hCaptcha or Turnstile response fields; or to any field in a form that has a card field identified that way, or inside Webflow's checkout form. Names outside these lists are protected only by the field's type and autocomplete. It logs a console warning instead of writing, and it won't add a hidden field whose name another field in the form already uses. With custom, use a selector.
fd-folder="FOLDER_ID"Saves uploads into a folder. The ID is the last part of the folder's address in Files.
fd-type="image"Built-in field. Which files can be picked: pdf, image, video, audio or document (Word, Excel, PowerPoint and PDF).
fd-multipleBuilt-in field. Allows more than one file; the links go into the field one after another. On a custom field, add multiple to your file input instead. No value.
fd-requiredBuilt-in field. The form can't be sent until a file is uploaded. No value.
fd-upload-text="Attach your CV"Built-in field. The text on the upload button. Default: Upload files.
fd-accept="image, .pdf"Custom field. Which files are allowed: the groups image, video, audio, code and docs, extensions like .pdf, or types like image/png, comma separated.
fd-max-size="10MB"Custom field. The largest file allowed: a number with B, KB, MB or GB. Larger files are refused before they upload.
fd-show-preview="cv"Custom field. Shows each file in the preview list with that name (see below).

Inside a custom field

AttributeWhat it does
fd-upload="trigger"On your button: opens the file picker.
multipleOn your <input type="file">: allows more than one file. No value.

On the preview list

AttributeWhat it does
fd-preview-list="cv"Names the list, so a field can point at it with fd-show-preview.
fd-prependNew files go to the top of the list instead of the bottom. No value.
fd-display="flex"How the list is shown once it has a file (any CSS display value). Default: block, so you can hide the list until then.

Inside a preview item

AttributeWhat it does
fd-preview="item"The template copied for each file. Everything below goes inside it.
fd-preview="img"On an <img>: a thumbnail for images, a file icon for anything else.
fd-preview="name"The file's name.
fd-preview="size"The file's size.
fd-preview="progress-fill"A bar whose width follows the upload.
fd-preview="progress-number"The upload's percentage, as text.
fd-preview="cancel"On a button: stops the upload, or removes the file and its link from the form.
fd-show-on-completeShown when the upload finishes. fd-hide-on-complete hides instead. No value.
fd-show-on-failShown when the upload fails. fd-hide-on-fail hides instead. No value.
fd-display="flex"On the item: how the elements above are shown (any CSS display value). Default: block.

Secured files

fd-secure and fd-download are for showing protected files, not for uploads. See Secured files.

Custom design

To use your own design instead of the built-in field, use fd-upload="custom" with your own button and file input, and an optional preview list:

html
<div fd-upload="custom" fd-input="#file_url" fd-show-preview="cv" fd-max-size="10MB" fd-accept="docs">
  <button type="button" fd-upload="trigger">Choose a file</button>
  <input type="file" hidden />
</div>

<div fd-preview-list="cv">
  <div fd-preview="item">
    <img fd-preview="img" alt="" />
    <span fd-preview="name"></span>
    <span fd-preview="size"></span>
    <div><div fd-preview="progress-fill"></div></div>
    <span fd-preview="progress-number"></span>
    <span fd-show-on-complete>Uploaded</span>
    <span fd-show-on-fail>Upload failed</span>
    <button type="button" fd-preview="cancel">Remove</button>
  </div>
</div>

<input id="file_url" type="hidden" name="file_url" />

Every attribute used here is explained in the Attributes reference.

Frameworks (React, Svelte, Vue)

The same script works in single-page apps. Upload fields that appear later, after a route change, in a modal or on a later form step, are set up when they appear.

Two things differ from a plain HTML page:

  1. Let your framework keep its element. On a plain page the widget replaces the <div>. A framework that rendered that div would break on its next update, so in an app the widget builds the uploader inside it. React is detected automatically. For Svelte, Vue and server-rendered apps, add fd-mount="inside".
  2. Read the link from the event. The widget fires fdupload on the element with the file's link.

React

React never runs onChange for hidden inputs, so listen for the event:

jsx
function UploadField({ onUploaded }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    const handle = (e) => onUploaded(e.detail.url);
    el.addEventListener('fdupload', handle);
    return () => el.removeEventListener('fdupload', handle);
  }, [onUploaded]);
  return <div ref={ref} fd-upload="default" fd-input="file_url" />;
}

Svelte

svelte
<script>
  let fileUrl = $state('');
</script>

<div
  fd-upload="default"
  fd-mount="inside"
  fd-input="file_url"
  onfdupload={(e) => (fileUrl = e.detail.url)}
></div>

Vue

vue
<template>
  <div
    fd-upload="default"
    fd-mount="inside"
    fd-input="file_url"
    @fdupload="(e) => (fileUrl = e.detail.url)"
  ></div>
</template>

Svelte's bind:value and Vue's v-model on the field named by fd-input also work: the widget fires input and change when it fills it in.

Events

All events bubble, so you can listen on the field or any parent.

EventWhenevent.detail
fduploadA file finished uploading{ url, name, size, type }
fdremoveThe visitor removed a file{ url, name }
fderrorAn upload failed{ name, code, message }

Troubleshooting

The upload button shows, but uploads fail. The site isn't on your upload key yet. On Webflow, turn on uploads for the site in the Flowdrive app, then publish. If uploads are already on and a domain is still refused, for example a custom domain you added in Webflow later, disable and enable uploads for that site in the app, then publish: Flowdrive reads the site's domains from Webflow each time uploads are turned on. On any other site, add it under Upload Widget → Your sites → Other sites (not built on Webflow); subdomains are included.

The field stays an empty box in my app. Check the script is in your page's <head>. Then use Check your page on the Upload Widget page: it confirms the script, your key and the field.

My React state doesn't update. Listen for fdupload as shown above. React doesn't run onChange for hidden inputs.

The script is on the page twice. Usually a copy pasted by hand plus the one the Webflow app adds. Remove the hand-pasted one; only one runs either way.

Take control of your file hosting on Webflow.

Ask about Flowdrive on
© 2026 Flowdrive