Upload Widget
Receive files from forms on your site, on Webflow or any framework
Last updated: October 5, 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 sites you've added under Upload Widget → Add your site. Subdomains are included, so acme.com also covers www.acme.com. Add your staging domain too if you test there (for example acme.webflow.io).
Until a site is added, the widget shows on your page but every upload is refused.
Using Webflow? Turn uploads on for a site with the Flowdrive app and its domains are added for you.
2. Install the widget
Webflow
In your dashboard, open Upload Widget → Webflow, connect your account and turn uploads on for your site. Then add the field from the Flowdrive app in the Webflow Designer and publish.
To add the field by hand instead, add a Div inside your form with the custom attributes fd-upload = default and fd-input = file_url.
Any other site
Add the script once, in your page's <head> or before </body>. Your upload key is on the Upload Widget page.
<script data-fd-id="YOUR_UPLOAD_KEY" src="https://files.tryflowdrive.com/index.min.js"></script>Then add the upload field inside your form:
<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.
Options
Add these to the upload field (fd-upload="default"):
| Attribute | What it does |
|---|---|
fd-input="file_url" |
Where the file's link goes. A name adds a hidden field with that name to the form; a selector like #cv uses a field you already have. |
fd-type="pdf" |
Which files can be picked: pdf, image, video, audio or document (Word, Excel, PowerPoint and PDF). |
fd-multiple |
Allow more than one file. The links are put in the field as <link> <link>. |
fd-required |
The form can't be sent without a file. |
fd-upload-text="Attach your CV" |
The text on the upload button. Default: Upload files. |
fd-folder="FOLDER_ID" |
Save uploads into a folder. The ID is the last part of the folder's address in Files. |
fd-mount="inside" |
For framework apps: keep your framework in charge of the element (see below). |
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:
- 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, addfd-mount="inside". - Read the link from the event. The widget fires
fduploadon the element with the file's link.
React
React never runs onChange for hidden inputs, so listen for the event:
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
<script>
let fileUrl = $state('');
</script>
<div
fd-upload="default"
fd-mount="inside"
fd-input="file_url"
onfdupload={(e) => (fileUrl = e.detail.url)}
></div>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.
| Event | When | event.detail |
|---|---|---|
fdupload |
A file finished uploading | { url, name, size, type } |
fdremove |
The visitor removed a file | { url, name } |
fderror |
An upload failed | { name, code, message } |
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:
<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" />| Attribute | On | What it does |
|---|---|---|
fd-upload="trigger" |
Your button | Opens the file picker. |
fd-input |
The widget | Selector of the field that receives the link. |
fd-show-preview |
The widget | Name of the preview list to use. |
fd-max-size |
The widget | Largest file allowed: a number and B, KB, MB or GB, like 10MB. |
fd-accept |
The widget | Allowed files: image, video, audio, code, docs, or extensions and types like .pdf or image/png, comma separated. |
fd-folder |
The widget | Save uploads into a folder. |
multiple |
The file input | Allow more than one file. |
fd-preview-list |
The list | The list's name. Add fd-prepend to put new files first, fd-display="flex" to choose how it's shown. |
fd-preview="item" |
Template | Copied for each file. Inside it: img, name, size, progress-fill, progress-number and cancel. |
fd-show-on-complete / fd-hide-on-complete |
Inside the item | Shown or hidden when the upload finishes. |
fd-show-on-fail / fd-hide-on-fail |
Inside the item | Shown or hidden when it fails. |
Troubleshooting
The upload button shows, but uploads fail. The site isn't added yet. Add it under Upload Widget → Add your site; 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.