# Validating File Types and Sizes Before Form Submission

File uploads introduce a few interesting validation problems.

With a normal text field, validation usually means checking whether the input is empty or whether the value matches a particular format.

With files, there are additional questions.

Is this file type allowed? Is the file too large? What happens if a customer selects multiple files and only one of them is invalid?

I've been working on file upload functionality for [Cruxtab Contact Form Builder](https://apps.shopify.com/cruxtab-form-builder), and these are some of the implementation details worth thinking about when building dynamic forms.

## Why validate files in the browser?

The browser is a useful place to provide early feedback.

If a customer selects a file that exceeds the allowed size, there's little reason to wait until the entire form is submitted before showing an error.

The same applies to file extensions.

Client-side validation makes the interface more responsive and helps customers fix problems while they're still working on the relevant field.

However, there's an important distinction: frontend validation improves usability, but it doesn't replace server-side validation.

## Checking file size with JavaScript

The browser's File API exposes a file's size in bytes through the `size` property.

Here's a simple helper:

```javascript
function isFileWithinLimit(file, maxSizeMB) {
  const maxBytes = maxSizeMB * 1024 * 1024;

  return file.size <= maxBytes;
}
```

If the configured limit is 5 MB, the function compares the file size against 5 MiB, or 5 × 1024 × 1024 bytes.

For production code, it's useful to keep the configured limit separate from the validation function. That makes the same logic reusable across different forms and plans.

## Where should the size limit come from?

Hardcoding a limit inside the JavaScript isn't ideal for a configurable form builder.

Different plans may have different upload allowances. The frontend needs to receive the appropriate setting for the current form.

In [Cruxtab Contact Form Builder](https://apps.shopify.com/cruxtab-form-builder), the relevant setting is `perFileUploadMB`, which is injected into the form's appearance settings.

The validation logic can then use that value when customers select files.

This keeps the limit configurable without requiring the validation code to be rewritten whenever the setting changes.

One detail to remember is that a per-file limit is different from a total upload limit. If a customer selects four files, each file might satisfy the per-file limit while the combined upload is still large. The system should account for total request limits separately where needed.

## Filtering file extensions

HTML provides an `accept` attribute that can guide file selection:

```html
<input
  type="file"
  multiple
  accept=".jpg,.jpeg,.png,.pdf"
>
```

This example suggests that customers should select JPG, PNG, or PDF files.

It's useful because the file picker can filter the displayed choices on supported browsers.

However, `accept` is not a security guarantee. A customer or client application can still provide an unexpected file.

The application should validate files again during the submission or upload process. Depending on the use case, that may include inspecting the file's content and checking its actual type instead of relying only on its name.

## Handling multiple files

When a field supports multiple files, validation should consider each selection individually.

For example:

```javascript
function validateFiles(files, maxSizeMB) {
  const results = Array.from(files).map(file => ({
    name: file.name,
    valid: isFileWithinLimit(file, maxSizeMB)
  }));

  return results;
}
```

This returns a result for each file, making it possible to identify which selections exceed the configured limit.

The UI can then show an appropriate message beside the relevant file.

In a complete implementation, invalid files should not accidentally remain in the collection sent for submission. The application also needs a clear policy for whether it rejects the entire selection or accepts valid files while asking the customer to remove invalid ones.

Consistency matters more than the particular policy chosen.

## Client-side checks aren't enough

This is the part I wouldn't skip.

A customer can modify JavaScript, bypass a browser restriction, or send a request directly to an endpoint. Therefore, any limits that matter for storage, security, or billing need to be enforced by the server or upload infrastructure.

For example, the submission workflow should account for:

*   Allowed file types
    
*   Maximum file size
    
*   Total request size
    
*   Upload authorization
    
*   Storage failures
    
*   Unexpected or malicious file content
    

The exact checks depend on how the upload endpoint and storage system are implemented.

The browser should help the customer avoid mistakes. The backend should decide whether the file is actually acceptable.

## How this fits into Cruxtab Contact Form Builder

The file upload field in [Cruxtab Contact Form Builder](https://apps.shopify.com/cruxtab-form-builder) uses the `file_upload` field type.

It supports multiple files, previews, removal, extension filtering, and client-side size checks using the plan's configured per-file upload limit.

The submission endpoint and AWS S3 are dependencies in the broader upload workflow. The field itself doesn't require a dedicated Shopify API scope.

Keeping field rendering, validation, submission, and storage as separate responsibilities makes the feature easier to maintain.

## Final thoughts

File validation isn't just about preventing oversized attachments.

It's about providing useful feedback in the browser while maintaining reliable checks throughout the submission pipeline.

For me, the key is to avoid treating frontend validation as the final authority. It should make the form easier to use, while the server remains responsible for enforcing the rules.

If you're building a file upload component, I'd be interested to hear how you handle per-file limits versus total submission limits.

More about the app: https://apps.shopify.com/cruxtab-form-builder
