Skip to main content
The File Uploader provides a powerful validation system that allows you to validate files both individually and as collections. The validation system supports synchronous and asynchronous validators with configurable timing and concurrency.

Built-in Validators

The library includes several built-in validators that run automatically based on your configuration:

Image Type Validation

Validates that uploaded files are images when imgOnly is enabled.
// Source: src/utils/validators/file/validateIsImage.ts
export const validateIsImage: FuncFileValidator = (outputEntry, api) => {
  const imagesOnly = api.cfg.imgOnly;
  const isImage = outputEntry.isImage;

  if (!imagesOnly || isImage) {
    return;
  }
  
  return {
    type: 'NOT_AN_IMAGE',
    message: api.l10n('images-only-accepted'),
    payload: { entry: outputEntry },
  };
};
Configuration:
<uc-config img-only="true"></uc-config>

File Type Validation

Validates file types against the accept attribute, supporting both MIME types and file extensions.
// Source: src/utils/validators/file/validateFileType.ts
const allowedFileTypes = mergeFileTypes([...(imagesOnly ? IMAGE_ACCEPT_LIST : []), accept]);
if (!allowedFileTypes.length) return;

const mimeOk = matchMimeType(mimeType, allowedFileTypes);
const extOk = matchExtension(fileName, allowedFileTypes);

if (!mimeOk && !extOk) {
  return {
    type: 'FORBIDDEN_FILE_TYPE',
    message: api.l10n('file-type-not-allowed'),
    payload: { entry: outputEntry },
  };
}
Configuration:
<uc-config accept="image/png,image/jpeg,.pdf"></uc-config>

File Size Validation

Validates that files don’t exceed the maximum size limit.
// Source: src/utils/validators/file/validateMaxSizeLimit.ts
export const validateMaxSizeLimit: FuncFileValidator = (outputEntry, api) => {
  const maxFileSize = api.cfg.maxLocalFileSizeBytes;
  const fileSize = outputEntry.size;
  
  if (maxFileSize && fileSize && fileSize > maxFileSize) {
    return {
      type: 'FILE_SIZE_EXCEEDED',
      message: api.l10n('files-max-size-limit-error', { 
        maxFileSize: prettyBytes(maxFileSize) 
      }),
      payload: { entry: outputEntry },
    };
  }
};
Configuration:
<!-- 5MB limit -->
<uc-config max-local-file-size-bytes="5242880"></uc-config>

Collection Validators

Validate the entire file collection for count limits.
// Source: src/utils/validators/collection/validateMultiple.ts
export const validateMultiple: FuncCollectionValidator = (collection, api) => {
  const total = collection.totalCount;
  const multipleMin = api.cfg.multiple ? api.cfg.multipleMin : 0;
  const multipleMax = api.cfg.multiple ? api.cfg.multipleMax : 1;

  if (multipleMin && total < multipleMin) {
    return {
      type: 'TOO_FEW_FILES',
      message: api.l10n('files-count-limit-error-too-few', {
        min: multipleMin,
        max: multipleMax,
        total,
      }),
      payload: { total, min: multipleMin, max: multipleMax },
    };
  }

  if (multipleMax && total > multipleMax) {
    return {
      type: 'TOO_MANY_FILES',
      message: api.l10n('files-count-limit-error-too-many', {
        min: multipleMin,
        max: multipleMax,
        total,
      }),
      payload: { total, min: multipleMin, max: multipleMax },
    };
  }
};
Configuration:
<uc-config 
  multiple="true" 
  multiple-min="2" 
  multiple-max="10">
</uc-config>

Custom Validators

File Validators

Create custom validators to implement your own validation logic.

Basic File Validator

import type { FuncFileValidator } from '@uploadcare/file-uploader';

const customValidator: FuncFileValidator = (outputEntry, api) => {
  // Validate file name pattern
  if (!outputEntry.name.match(/^[a-zA-Z0-9-_]+\./)) {
    return {
      message: 'File name must contain only alphanumeric characters',
    };
  }
  
  // No error - file is valid
  return undefined;
};
<uc-config id="my-config" pubkey="YOUR_PUBLIC_KEY"></uc-config>

<script>
  const config = document.getElementById('my-config');
  config.fileValidators = [customValidator];
</script>

Async File Validator

const asyncValidator: FuncFileValidator = async (outputEntry, api, options) => {
  // Access abort signal for cancellation
  const { signal } = options || {};
  
  try {
    // Perform async validation (e.g., check with external API)
    const response = await fetch('/api/validate-file', {
      method: 'POST',
      body: JSON.stringify({ 
        name: outputEntry.name,
        size: outputEntry.size 
      }),
      signal,
    });
    
    const result = await response.json();
    
    if (!result.valid) {
      return {
        message: result.error || 'File validation failed',
      };
    }
  } catch (error) {
    if (error.name === 'AbortError') {
      // Validation was cancelled
      return;
    }
    // Handle other errors
    console.error('Validation error:', error);
  }
};

Validator Descriptors

Control when validators run with descriptor objects.
import type { FileValidatorDescriptor } from '@uploadcare/file-uploader';

const validators: FileValidatorDescriptor[] = [
  {
    runOn: 'add',
    validator: (outputEntry, api) => {
      // Runs once when file is added
      console.log('Validating on add:', outputEntry.name);
    },
  },
  {
    runOn: 'upload',
    validator: (outputEntry, api) => {
      // Runs once when upload starts
      console.log('Validating on upload:', outputEntry.name);
    },
  },
  {
    runOn: 'change',
    validator: (outputEntry, api) => {
      // Runs on every file change (status, URL, etc.)
      console.log('Validating on change:', outputEntry.status);
    },
  },
];

config.fileValidators = validators;
Validator Timing:
Run OnWhen It ExecutesUse Case
addOnce when file is addedQuick client-side checks (name, extension)
uploadOnce when upload startsPre-upload validation after file info is available
changeOn every state changeContinuous validation during and after upload
If you pass a function directly (not a descriptor), it defaults to runOn: 'change'.

Collection Validators

Validate the entire collection of files.
import type { FuncCollectionValidator } from '@uploadcare/file-uploader';

const collectionValidator: FuncCollectionValidator = (collection, api) => {
  // Check total file size
  const totalSize = collection.successEntries.reduce(
    (sum, entry) => sum + entry.size, 
    0
  );
  
  const maxTotalSize = 50 * 1024 * 1024; // 50MB
  
  if (totalSize > maxTotalSize) {
    return {
      message: `Total file size exceeds ${maxTotalSize / 1024 / 1024}MB`,
    };
  }
  
  // Ensure at least one image
  const hasImage = collection.allEntries.some(entry => entry.isImage);
  if (!hasImage) {
    return {
      message: 'At least one image file is required',
    };
  }
};

config.collectionValidators = [collectionValidator];

Validation Configuration

Timeout Settings

<!-- Set validation timeout to 5 seconds -->
<uc-config validation-timeout="5000"></uc-config>
// Source: src/abstract/managers/ValidationManager.ts:212-216
const timeoutId = setTimeout(() => {
  state.skippedValidators.add(validatorDescriptor.validator);
  abortController.abort();
  console.warn(LOG_TEXT.FILE_VALIDATION_TIMEOUT);
}, timeoutMs);

Concurrency Control

<!-- Validate up to 5 files concurrently -->
<uc-config validation-concurrency="5"></uc-config>
// Source: src/abstract/managers/ValidationManager.ts:107-110
this._ctx.sub(sharedConfigKey('validationConcurrency'), (concurrency) => {
  this._queue.concurrency = concurrency;
});

Error Handling

Error Types

All validation errors have a consistent structure:
// Source: src/types/exported.ts:358-372
export type OutputFileErrorType =
  | 'CUSTOM_ERROR'
  | 'NOT_AN_IMAGE'
  | 'FORBIDDEN_FILE_TYPE'
  | 'FILE_SIZE_EXCEEDED'
  | 'UPLOAD_ERROR'
  | 'NETWORK_ERROR'
  | 'UNKNOWN_ERROR';

export type OutputCollectionErrorType =
  | 'CUSTOM_ERROR'
  | 'SOME_FILES_HAS_ERRORS'
  | 'TOO_MANY_FILES'
  | 'TOO_FEW_FILES';

Accessing Errors

const api = ctxProvider.getAPI();
const entry = api.addFileFromObject(file);

// Access file-level errors
const fileEntry = api.getOutputItem(entry.internalId);
console.log('File errors:', fileEntry.errors);
// Example: [{ type: 'FILE_SIZE_EXCEEDED', message: '...', payload: {...} }]

// Access collection-level errors
const collection = api.getOutputCollectionState();
console.log('Collection errors:', collection.errors);
// Example: [{ type: 'TOO_MANY_FILES', message: '...', payload: {...} }]

Custom Error Types

const validator: FuncFileValidator = (outputEntry) => {
  if (outputEntry.name.includes('test')) {
    return {
      type: 'CUSTOM_ERROR',
      message: 'Test files are not allowed',
      payload: {
        customField: 'Additional error data',
      },
    };
  }
};
If your validator doesn’t specify a type, it will automatically be set to 'CUSTOM_ERROR'.

Validation States

Files expose validation state through their properties:
interface OutputFileEntry {
  isValidationPending: boolean; // True while validation is running
  errors: OutputErrorFile[]; // Array of validation errors
  status: 'idle' | 'uploading' | 'success' | 'failed' | 'removed';
}
// Monitor validation state
const entry = api.getOutputItem(internalId);

if (entry.isValidationPending) {
  console.log('Validation in progress...');
}

if (entry.isFailed) {
  console.log('File failed validation:', entry.errors);
}

Best Practices

  • Use runOn: 'add' for quick, synchronous checks
  • Use runOn: 'upload' for slower validations that need file data
  • Keep runOn: 'change' validators lightweight
  • Set appropriate validation timeout to prevent blocking
  • Use validation concurrency to control resource usage

Complete Example

<!DOCTYPE html>
<html>
<head>
  <script type="module">
    import * as UC from 'https://cdn.jsdelivr.net/npm/@uploadcare/file-uploader@latest/web/file-uploader.min.js';
    UC.defineComponents(UC);
    
    // Custom validators
    const fileNameValidator = (entry) => {
      if (entry.name.length > 100) {
        return { message: 'File name too long (max 100 characters)' };
      }
    };
    
    const asyncImageValidator = async (entry, api, { signal }) => {
      if (!entry.isImage) return;
      
      try {
        const response = await fetch(entry.cdnUrl, { signal });
        const blob = await response.blob();
        const img = await createImageBitmap(blob);
        
        if (img.width < 800 || img.height < 600) {
          return {
            message: 'Image must be at least 800x600 pixels',
          };
        }
      } catch (err) {
        if (err.name !== 'AbortError') {
          console.error('Image validation failed:', err);
        }
      }
    };
    
    const collectionValidator = (collection) => {
      const imageCount = collection.allEntries.filter(e => e.isImage).length;
      if (imageCount === 0) {
        return { message: 'At least one image is required' };
      }
    };
    
    // Apply validators
    window.addEventListener('DOMContentLoaded', () => {
      const config = document.getElementById('config');
      
      config.fileValidators = [
        { runOn: 'add', validator: fileNameValidator },
        { runOn: 'upload', validator: asyncImageValidator },
      ];
      
      config.collectionValidators = [collectionValidator];
    });
  </script>
</head>
<body>
  <uc-file-uploader-regular ctx-name="uploader"></uc-file-uploader-regular>
  <uc-config 
    id="config"
    ctx-name="uploader"
    pubkey="YOUR_PUBLIC_KEY"
    multiple="true"
    multiple-min="1"
    multiple-max="10"
    max-local-file-size-bytes="10485760"
    validation-timeout="10000"
    validation-concurrency="3"
    accept="image/*"
  ></uc-config>
  <uc-upload-ctx-provider ctx-name="uploader"></uc-upload-ctx-provider>
</body>
</html>

API Reference

Types

// Source: src/types/exported.ts
export type FuncFileValidator = (
  outputEntry: OutputFileEntry,
  api: UploaderPublicApi,
  options?: { signal?: AbortSignal },
) => undefined | OutputErrorFile | Promise<undefined | OutputErrorFile>;

export type FileValidatorDescriptor = {
  runOn: 'add' | 'upload' | 'change';
  validator: FuncFileValidator;
};

export type FileValidator = FileValidatorDescriptor | FuncFileValidator;

export type FuncCollectionValidator = (
  collection: OutputCollectionState,
  api: UploaderPublicApi,
) => undefined | OutputErrorCollection;

Configuration Properties

PropertyTypeDefaultDescription
fileValidatorsFileValidator[][]Array of file validators
collectionValidatorsFuncCollectionValidator[][]Array of collection validators
validationTimeoutnumber(default)Timeout for async validators in milliseconds
validationConcurrencynumber20Number of files to validate concurrently

Build docs developers (and LLMs) love