To build a file upload form in HTML without a server, add enctype="multipart/form-data" and a file input, and set the form's action to a FormSubmit endpoint. Add the multiple attribute to let visitors upload several files at once. FormSubmit stores each file privately, links it in your notification email and dashboard, and passes signed links to your integrations.
Basic upload form
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST" enctype="multipart/form-data" class="fs-form">
<div class="fs-field">
<label for="fs-name" class="fs-label">Name</label>
<input id="fs-name" type="text" name="name" required class="fs-input">
</div>
<div class="fs-field">
<label for="fs-email" class="fs-label">Email</label>
<input id="fs-email" type="email" name="email" required class="fs-input">
</div>
<div class="fs-field">
<label for="fs-files" class="fs-label">Files</label>
<input id="fs-files" type="file" name="files" multiple required class="fs-input">
</div>
<div class="fs-field">
<label for="fs-note" class="fs-label">Note</label>
<textarea id="fs-note" name="note" rows="5" class="fs-input"></textarea>
</div>
<!-- Honeypot: leave this hidden field empty to catch bots -->
<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
<!-- Optional: where to send people after submitting -->
<!-- <input type="hidden" name="_redirect" value="https://yoursite.com/thanks"> -->
<button type="submit" class="fs-button">Send</button>
</form>
<style>
.fs-form { max-width: 32rem; margin: 0 auto; display: grid; gap: 1.1rem; font-family: system-ui, sans-serif; }
.fs-field { display: grid; gap: 0.35rem; border: 0; padding: 0; margin: 0; }
.fs-label { font-size: 0.875rem; font-weight: 600; color: #18181b; }
.fs-input { width: 100%; box-sizing: border-box; border: 1px solid #d4d4d8; border-radius: 0.5rem; padding: 0.6rem 0.75rem; font: inherit; font-size: 0.9rem; }
.fs-input:focus { outline: 2px solid #6366f155; border-color: #6366f1; }
.fs-choice { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; }
.fs-help { font-size: 0.75rem; color: #71717a; margin: 0; }
.fs-button { background: #18181b; color: #fff; border: 0; border-radius: 0.5rem; padding: 0.7rem 1rem; font-weight: 600; cursor: pointer; }
.fs-button:hover { background: #27272a; }
</style>Key details:
method="POST"andenctype="multipart/form-data"are both required. Without the enctype, the browser only sends the file name.- Each file input needs a
name. That name labels the attachment in your email and dashboard. - Other fields (name, email, message) are sent in the same request, so one submission holds both the files and the details.
A minimal version looks like this:
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST" enctype="multipart/form-data">
<label>Email <input type="email" name="email" required></label>
<label>Resume <input type="file" name="resume" accept=".pdf,.doc,.docx" required></label>
<input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
<button type="submit">Upload</button>
</form>HTML form with multiple file upload
To accept several files in one field, add the multiple attribute. Visitors can then shift- or ctrl-click several files in the picker, or drop several on the input:
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST" enctype="multipart/form-data">
<label>Your email <input type="email" name="email" required></label>
<!-- One input, many files -->
<label>Photos <input type="file" name="photos" accept="image/*" multiple></label>
<!-- Separate inputs for different documents -->
<label>ID document <input type="file" name="id_document" accept=".pdf,image/*"></label>
<label>Proof of address <input type="file" name="proof_of_address" accept=".pdf"></label>
<input type="text" name="_gotcha" style="display:none" tabindex="-1" autocomplete="off">
<button type="submit">Send files</button>
</form>How multiple files are sent and named
With multiple, the browser sends one multipart part per selected file, and every part uses the input's name. FormSubmit stores each part as a separate attachment, labelled with that field name. So three photos in the example above show up as three files under photos.
You'll often see name="photos[]" in tutorials. The brackets are a convention for server frameworks like PHP, which need them to collect several values under one key. FormSubmit doesn't need them. Both photos and photos[] keep every file, but the files are labelled with the name exactly as written, so photos reads better in your email and dashboard.
When should you use one multiple input, and when separate inputs?
| Use | When |
|---|---|
One input with multiple | The files are the same kind of thing: photos, screenshots, portfolio pieces |
| Separate named inputs | Each file means something different: resume vs cover letter, front vs back of an ID |
Multiple file upload limits
- Up to 20 files per submission.
- Each file must be within your plan's per-file limit (see below).
- The whole request has to stay under about 4.5 MB when you post plain HTML. That total is what most often trips up multiple-file forms: five 1.5 MB phone photos are already too much. For bigger batches, use direct uploads.
You can't cap the number of files with HTML attributes alone. If you need to, check input.files.length in a small script before submitting.
Filter file types with accept
The accept attribute filters what the file picker shows. It takes extensions, MIME types or wildcards, separated by commas:
<input type="file" name="resume" accept=".pdf,.doc,.docx">
<input type="file" name="photos" accept="image/*" multiple>
<input type="file" name="logo" accept="image/png,image/svg+xml">accept is a usability hint, not a security control. Visitors can switch the picker to "All files", and bots ignore it entirely. FormSubmit accepts any file type, stores it privately and always serves files other than images and PDFs as downloads rather than opening them in the browser.
File size limits
| Plan | Per file | Total storage |
|---|---|---|
| Free | 10 MB | 100 MB |
| Starter | 10 MB | 2 GB |
| Pro | 25 MB | 10 GB |
| Business | 25 MB | 25 GB |
Plain multipart submissions are capped at about 4.5 MB per request in total by our serverless infrastructure. Larger requests fail with payload_too_large, and a single file over your plan's limit fails with file_too_large.
For larger files, or several files that add up past 4.5 MB, upload directly from the browser to storage and submit references to them instead. Each file can then be as large as your plan allows. See direct uploads for the code, which loops over every file in every input, so multiple works without changes. If you'd rather not write it yourself, FormSubmit also serves an optional helper script at https://formsubmit.app/formsubmit.js that adds AJAX submission and direct uploads to an existing form. Check the Install tab in your dashboard for the current snippet. For more on sending forms from JavaScript, see submitting a form with JavaScript.
To warn visitors before they hit a limit, check sizes in the browser:
<script>
document.querySelector("form").addEventListener("submit", (e) => {
const files = [...e.target.querySelectorAll('input[type="file"]')].flatMap((i) => [...i.files]);
const total = files.reduce((n, f) => n + f.size, 0);
if (total > 4.5 * 1024 * 1024) {
e.preventDefault();
alert("Those files are too large to send together. Please send fewer or smaller files.");
}
});
</script>A job application form
The job application template is a good starting point: name, email, phone, LinkedIn, position, resume upload and cover letter.
Where do the files go?
- Your notification email lists each attachment as a link.
- In the dashboard, open a submission to preview images and PDFs or download any file.
- Webhooks receive a
filesarray with each file'sfield,filename,url,sizeandcontentType. Slack, Discord, Airtable and Google Sheets receive signed links.
Files are private, count toward your storage quota, and are deleted with their submission, either manually or through retention. Files attached to plain HTML submissions that are flagged as spam aren't stored. For security considerations, from file type checks to retention, read secure file upload forms.