> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://uploadcare.com/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://uploadcare.com/docs/_mcp/server.

# Collection validators

> Implement custom collection-level validators to enforce rules across the entire selection (e.g., count, types, or cross-file constraints).

## What is a collection validator?

A collection validator is a rule that checks the whole selection of files at once. Use it when your logic depends on relationships between files rather than a single file.

* If the selection breaks your rule, return an error object with a message users can see.
* If everything is okay, return nothing and the selection is accepted.

Unlike file validators, collection validators are synchronous and there is no validation queue.

### How it can be used

* Prevent duplicates across the selection (by name, size, hash, or another stable key you compute).
* Ensure uniform type across all files (for example, all entries share the same top-level MIME like `image/*` or `application/*`).
* Cross-file dependencies (for example, if a CSV is present, a README must also be present).

### What users will see

When the selection doesn’t meet your rule, the uploader shows a clear message explaining what to fix.

## Validation model and signature

Collection validators are synchronous functions:

```ts
type CollectionValidator = (
  collection: OutputCollectionState,
  api: UploaderPublicApi
) => OutputErrorCollection | undefined;
```

See also: [OutputCollectionState](/docs/file-uploader/api/#output-collection-state), [UploaderPublicApi](/docs/file-uploader/api/)

Error type returned on failure:

```ts
type OutputErrorCollection = {
  message: string;
  payload?: Record<string, unknown>;
};
```

Here, `payload` is an optional object where you can include extra details about the error (for example, actual vs expected dimensions) and then catch them in your app via events or the API.

## When it runs

Collection validators re-run on specific selection-related changes:

* Triggers:
  * When files are added to or removed from the list
  * When any file’s `errors` list changes (for example, a file becomes valid/invalid after file-level validation, or uploading fails)
* Not triggered: general file state changes (upload progress, post-upload edits, uploading success) unless they cause an `errors` change

Implications:

* Do not rely on properties that appear only after upload (for example, `fileInfo`, `cdnUrl`, `cdnUrlModifiers`).
* It’s fine to base collection rules on whether files passed/failed file validators by inspecting `entry.errors`.
* Prefer rules that can be decided from information available at selection time (filenames, sizes, MIME types, or flags already present on entries).
* If you need to validate post-upload information directly, use a [file validator](/docs/file-uploader/file-validators) with an appropriate `runOn` and check the whole collection for custom errors there.

## Examples

### Specified mix of file types

```ts
const specifiedMix: CollectionValidator = (collection, api) => {
  const entries = collection.allEntries;
  const hasCover = entries.some((e) =>
    /cover\.(jpg|png|jpeg)$/i.test(e.fileName ?? "")
  );
  const galleryCount = entries.filter((e) =>
    /gallery\.(jpg|png|jpeg)$/i.test(e.fileName ?? "")
  ).length;
  if (!hasCover || galleryCount < 3) {
    return {
      message:
        "Please include one cover image and at least three gallery images",
    };
  }
};

const config = document.querySelector("uc-config");
config.collectionValidators = [specifiedMix];
```

### No duplicates in the selection

```ts
const noDuplicates: CollectionValidator = (collection, api) => {
  const seen = new Set<string>();
  for (const e of collection.allEntries) {
    const key = `${e.fileName?.toLowerCase()}::${e.size || 0}`;
    if (seen.has(key)) {
      return { message: "The selection contains duplicate files" };
    }
    seen.add(key);
  }
};

const config = document.querySelector("uc-config");
config.collectionValidators = [noDuplicates];
```

### Uniform top-level MIME type

```ts
const uniformTopLevelMime: CollectionValidator = (collection, api) => {
  const top = (m: string) => m.split("/")[0] ?? "unknown";
  const types = new Set(
    collection.allEntries
      .filter((e) => !!e.mimeType)
      .map((e) => top(e.mimeType))
  );
  if (types.size > 1) {
    return { message: "Please select files of the same top-level type" };
  }
};

const config = document.querySelector("uc-config");
config.collectionValidators = [uniformTopLevelMime];
```

### CSV requires a README in the selection

```ts
const csvRequiresReadme: CollectionValidator = (collection, api) => {
  const hasCsv = collection.allEntries.some((e) =>
    /\.csv$/i.test(e.fileName ?? "")
  );
  if (!hasCsv) return;
  const hasReadme = collection.allEntries.some((e) =>
    /README(\.[^.]+)?$/i.test(e.fileName ?? "")
  );
  if (!hasReadme) {
    return {
      message: "If a CSV is present, include a README in the selection",
    };
  }
};

const config = document.querySelector("uc-config");
config.collectionValidators = [csvRequiresReadme];
```

## Error localization

Show validation messages in the user’s language:

* Single-language projects: return plain strings from your validators.
* Multi-language projects: define localization keys, extend your locales, and reference those keys from validators.

Learn how to set up keys and extend locales: [Localization](/docs/file-uploader/validation-localization)

## Best practices

* Keep rules fast and deterministic; collection validators are synchronous.
* Prefer clear, actionable messages so users know how to fix the selection.
* Combine with file validators for per-file checks (dimensions, metadata) and use collection validators for cross-file logic (duplicates, uniform types, cross-file dependencies).