---
title: "Query Presets"
aliases: [query-presets, payload-query-presets, saved-filters]
tags: [payloadcms, admin, collections, access-control, list-view]
sources: [raw/query-presets__overview.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

Query Presets allow users to **save and share filters, columns, and sort orders** for Collections. Each preset is stored as a record in the `payload-query-presets` built-in collection — so presets are user-defined at runtime, not hard-coded in config.

Presets are applied in the **Admin Panel List View**. When enabled, extra controls appear for managing presets.

## Enabling on a Collection

```ts
import type { CollectionConfig } from 'payload'

export const MyCollection: CollectionConfig = {
  slug: 'my-collection',
  enableQueryPresets: true,  // <-- this is all that's required
}
```

## Config Options

All settings live under `queryPresets` at the root of `buildConfig`:

```ts
const config = buildConfig({
  queryPresets: {
    access: { ... },          // collection-level access control
    filterConstraints: ...,   // dynamically filter which constraints are available
    constraints: { ... },     // document-level custom access rules
    labels: { ... },          // custom UI labels
  },
})
```

| Option | Description |
|--------|-------------|
| `access` | Collection-level access — applies to all presets, statically defined |
| `filterConstraints` | Dynamically hide/disable constraint options per user |
| `constraints` | Document-level custom access control options (RBAC etc.) |
| `labels` | Custom labels for the presets collection in the admin UI |

## Access Control

Two layers of access control exist:

### 1. Collection Access Control

Applies to **all** presets. Not user-controllable — defined statically in config.

```ts
queryPresets: {
  access: {
    read: ({ req: { user } }) =>
      user?.roles?.some((role) => role === 'admin') ?? false,
    update: ({ req: { user } }) =>
      user?.roles?.some((role) => role === 'admin') ?? false,
  },
}
```

> **Warning:** Custom access overrides defaults — including the authentication requirement. Always add an explicit auth check unless public access is intended.

### 2. Document Access Control

Applies per-preset. Users can configure these in the Admin Panel UI. Built-in options:

- **Only Me** — only the creator can read/update/delete
- **Everyone** — all authenticated users
- **Specific Users** — select individual users

#### Custom Constraints (RBAC example)

```ts
queryPresets: {
  constraints: {
    read: [
      {
        label: 'Specific Roles',
        value: 'specificRoles',
        fields: [
          {
            name: 'roles',
            type: 'select',
            hasMany: true,
            options: [
              { label: 'Admin', value: 'admin' },
              { label: 'User', value: 'user' },
            ],
          },
        ],
        // Payload wraps custom fields inside `access[operation]` group
        access: ({ req: { user } }) => ({
          'access.read.roles': { in: [user?.roles] },
        }),
      },
    ],
  },
}
```

Each constraint entry requires:

| Option | Description |
|--------|-------------|
| `label` | Display name in the dropdown |
| `value` | DB-stored identifier |
| `fields` | Fields rendered when this option is selected |
| `access` | Function returning the Where query that enforces the rule |

> **Gotcha:** Custom fields are stored under `access[operation]`, so filter paths must be prefixed accordingly (e.g. `access.read.roles`).

### Filter Constraints

Dynamically control which constraint options are available based on the current user:

```ts
queryPresets: {
  filterConstraints: ({ req, options }) =>
    !req.user?.roles?.includes('admin')
      ? options.filter(
          (option) =>
            (typeof option === 'string' ? option : option.value) !== 'everyone',
        )
      : options,
}
```

This example prevents non-admin users from setting a preset to "everyone". Hidden options are disabled (not removed) if a preset already uses them.

## Key Takeaways

- Enable with a single `enableQueryPresets: true` on any Collection — no schema changes needed
- Presets are stored as records in the auto-created `payload-query-presets` collection
- Two access layers: **collection-level** (static, admin-set) and **document-level** (user-set per preset)
- Custom constraints enable RBAC — use `access[operation].fieldName` path prefix for Where queries
- `filterConstraints` lets you hide constraint options from users without the right permissions
- Overriding `queryPresets.access` removes the default auth requirement — always add explicit user checks

## Related

- [[wiki/payloadcms/collection-config|Collection Config]] — `enableQueryPresets` property
- [[wiki/payloadcms/access-control|Access Control]] — general Payload access control patterns
- [[wiki/payloadcms/custom-components-list-view|Custom Components — List View]] — other List View customizations
- [[wiki/payloadcms/features|Features — Email, Folders, Trash, Query Presets]] — high-level feature overview

## Sources

- `raw/query-presets__overview.md`
- https://payloadcms.com/docs/query-presets/overview
