splitforms.com
← Back to the journal

How to Validate File Size and Type Before Upload in JavaScript

Validate file size and type in JavaScript before upload: file.size in bytes, MB limits, accept is not enforced, setCustomValidity, and multiple files.

Start free — 500 submissionsSee pricing →No card, no time limit. Pro is $5 per month.
Short answer
There is no HTML max-size on file inputs. On change, read each file.size (bytes), compare to your MB limit, and use setCustomValidity. accept is not enforced — check file.type and the extension too. Sum sizes when multiple is set, and mirror every rule on the server.
Try it on your own form

Point any HTML form at one endpoint. 500 free submissions — no card, no time limit — and unlimited forms.

Create free account

HTML has no max-size attribute

People search for input type="file" max size, but the HTML spec does not define a maxsize or max-size attribute on <input type="file">. Attributes like required and accept exist; a byte limit does not. The browser will happily POST a 200 MB file if your backend allows it.

<!-- Valid HTML — but no built-in size cap -->
<input type="file" name="attachment" accept=".pdf,image/*" required />

Client-side checks save bandwidth and give instant feedback; they are not security. Treat JavaScript validation as UX and enforce limits again when the bytes hit your server (see file uploads on contact forms).

Check file.size on change with setCustomValidity

File.size is in bytes. Five megabytes is 5 * 1024 * 1024 (5,242,880), not 5 * 1000 * 1000. Listen for change on the input, loop input.files, and call setCustomValidity so native form validation blocks submit:

const MAX_BYTES = 5 * 1024 * 1024; // 5 MB
const input = document.querySelector('input[type="file"]');

input.addEventListener("change", () => {
  let tooBig = false;
  for (const file of input.files) {
    if (file.size > MAX_BYTES) tooBig = true;
  }
  input.setCustomValidity(
    tooBig ? "File is larger than 5 MB" : ""
  );
});

Clear the message when the user picks a valid file, or the field stays invalid after they fix the selection. Pair this with reportValidity() if you validate on submit instead of on every change.

accept filters the picker — it is not enforced

The accept attribute narrows the file dialog on most desktops. Mobile and desktop users can still bypass it. Do not trust file.type alone either — the browser sets it from the extension. Check MIME type and the filename:

const ALLOWED_TYPES = ["application/pdf", "image/png", "image/jpeg"];

function typeOk(file) {
  if (ALLOWED_TYPES.includes(file.type)) return true;
  const name = file.name.toLowerCase();
  return /\.(pdf|png|jpe?g)$/i.test(name);
}

input.addEventListener("change", () => {
  const bad = [...input.files].some((f) => !typeOk(f));
  input.setCustomValidity(bad ? "Only PDF or PNG/JPEG images" : "");
});

Multiple files: add up the total size

With the multiple attribute, input.files is a list. Apply per-file limits and a total cap so one submission cannot exceed your backend quota:

const MAX_PER_FILE = 5 * 1024 * 1024;
const MAX_TOTAL = 15 * 1024 * 1024;

input.addEventListener("change", () => {
  let total = 0;
  for (const file of input.files) {
    if (file.size > MAX_PER_FILE) {
      input.setCustomValidity("Each file must be 5 MB or smaller");
      return;
    }
    total += file.size;
  }
  input.setCustomValidity(
    total > MAX_TOTAL ? "Total upload must be 15 MB or less" : ""
  );
});

Show a readable size (KB / MB)

Error text like "5242880 bytes" confuses users. Format file.size for display:

function formatBytes(bytes) {
  if (bytes < 1024) return bytes + " B";
  if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
  return (bytes / (1024 * 1024)).toFixed(1) + " MB";
}

// "resume.pdf is 8.2 MB (max 5 MB)"
input.setCustomValidity(
  `${file.name} is ${formatBytes(file.size)} (max 5 MB)`
);

Optional: check image width and height

For avatars or banners you may need minimum dimensions, not just bytes. createImageBitmap reads dimensions without inserting an <img> into the page:

async function imageDimensionsOk(file) {
  if (!file.type.startsWith("image/")) return true;
  const bitmap = await createImageBitmap(file);
  const ok = bitmap.width >= 800 && bitmap.height >= 600;
  bitmap.close();
  return ok;
}

Run this inside your change handler (with await) before you call setCustomValidity. Large photos can be small in bytes but huge in pixels — combine dimension checks with a byte cap.

Always enforce limits on the server

Client checks are trivial to bypass. Your form backend, CDN, and app server each have their own upload limits (413 errors, proxy body size, function payload caps). Reject oversize streams early, sniff file types, and match the rules you showed in the UI. Walkthroughs: add file uploads to a contact form, static site file upload forms, and the <input type="file"> reference. If you need a form that accepts the files once they pass validation, see how to build a file upload form.

// Pseudocode — stop reading once over limit
let received = 0;
for await (const chunk of request.body) {
  received += chunk.length;
  if (received > MAX_BYTES) {
    return new Response("File too large", { status: 413 });
  }
}

FAQ

How do I limit file upload size in HTML?

HTML has no max-size attribute on input type=file. Use JavaScript: read file.size in bytes on the change event, compare to your limit (for example 5 * 1024 * 1024 for 5 MB), and call setCustomValidity with an error message or an empty string when the file is OK. Always enforce the same limit on the server.

Does the accept attribute stop wrong file types?

No. accept only filters the OS file picker UI. Users can choose All files or rename extensions. Check file.type and the filename extension in JavaScript for early feedback, then verify magic bytes and size on the server.

How do I validate multiple files at once?

When the input has the multiple attribute, loop input.files and sum each file.size. Reject the selection if the total exceeds your cap or if any single file is too large. Show one setCustomValidity message on the input for the whole control.

What is file.size in JavaScript?

file.size is the File object's size in bytes (not kilobytes). Compare it directly to limits like 5 * 1024 * 1024. Use a small helper to display KB or MB in error text so users understand the limit.

References: MDN: File.size and MDN: setCustomValidity.

Need a backend that already caps uploads and stores files safely? splitforms handles multipart POSTs from plain HTML — connect Storage and keep the same setCustomValidity UX on the client.

Related articles

More practical guidance from tutorials.

Browse the journal →
Tutorials

How to Remove the Arrows From a Number Input (CSS, Tailwind, React)

Hide the number input spin buttons with ::-webkit-inner-spin-button and appearance: textfiel…

7 min readRead →
Tutorials

How to Change the Autofill Background Color of an Input (CSS, Tailwind)

Remove Chrome's yellow autofill background with input:-webkit-autofill, inset box-shadow, -w…

7 min readRead →
Tutorials

input type=number maxlength Not Working? 4 Fixes That Work

Why maxlength is ignored on input type=number, plus four fixes: min/max, oninput slice, text…

8 min readRead →

Explore this topic

Start with the overview, then move into focused guides.

OverviewContact Forms for Every FrameworkStart here →GuideContact Form for Next.jsRead →GuideContact Form for ReactRead →GuideContact Form for VueRead →ReferenceContact form guideOpen →

Building forms with ChatGPT, Claude, Cursor, or v0? Connect the native MCP server and give your agent a production form backend.

Explore the MCP server →

Next steps

Put this article into practice with the matching tool or guide.

Give your form a production backend.

One endpoint adds delivery, spam filtering, storage, and integrations. Start with 500 free submissions — no card, no time limit.

Create free accountRead the docs →
Secure checkoutSSL encryptionPrivacyProtected
VISAAMERICANEXPRESSstripe