# Globals

> This skill should be used when the user asks to "create a Payload global", "add site settings", "build a header/footer global", "define a GlobalConfig", "set global access control", "add hooks to a global", "fetch a global with the Local API", or needs to model any single-instance configuration (nav, homepage, announcement banner) in PayloadCMS v3.

- Skill: `agents-store/globals` (Agent Skill)
- Install (CLI): `npx skillmds@latest add agents-store/globals`
- Raw SKILL.md: https://api.skillmd.com/api/skills/agents-store/globals/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: agents-store (https://skillmd.com/u/agents-store)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/agents-store/globals

---


# PayloadCMS — Globals

A Global is a **singleton** document — exactly one instance exists for its slug. Where a collection holds many rows, a Global holds one: site settings, the header, the footer, primary navigation, a homepage config, or an announcement banner. Like a collection, each Global is a typed document, a REST/GraphQL endpoint, an admin panel page, and an auto-generated TypeScript type — all from one config object.

## Global vs. single-doc collection

Reach for a **Global** when there is conceptually one and only one record, edited in place with no list view and no create/delete. Reach for a **collection** when you have (or might have) more than one — even "one per locale" or "one per tenant" is still a collection. If two Globals would share an identical field structure, model them as one collection instead.

## Minimal Global

```ts
// src/globals/Settings.ts
import type { GlobalConfig } from 'payload'

export const Settings: GlobalConfig = {
  slug: 'settings',
  label: 'Site Settings',
  fields: [
    { name: 'siteName', type: 'text', required: true },
    { name: 'siteDescription', type: 'textarea' },
  ],
}
```

Register it in `payload.config.ts`:
```ts
// src/payload.config.ts
import { Settings } from './globals/Settings'
import { Header } from './globals/Header'

export default buildConfig({
  globals: [Settings, Header],
})
```

Re-run `pnpm generate:types` after every global change. The slug `settings` produces a `Setting` (or `Settings`) interface in `payload-types.ts`.

## GlobalConfig Reference

| Property | Type | Purpose |
| --- | --- | --- |
| `slug` | `string` | **Required.** Unique key. Becomes `/api/globals/<slug>`, the DB table, and the type name. |
| `fields` | `Field[]` | **Required.** Document schema. See the `fields` skill. |
| `label` | `string` | Display name in the admin panel. Defaults to an inflected slug. |
| `description` | `string \| Component` | Helper text/React component shown under the header for editor context. |
| `access` | `object` | Permission rules — `read` / `update` only. See below. |
| `hooks` | `object` | Lifecycle callbacks. See below. |
| `versions` | `boolean \| object` | Enable version history / drafts. |
| `admin` | `object` | Admin panel behavior (group, description, preview, livePreview). |
| `endpoints` | `Endpoint[]` | Custom REST routes scoped to this global. |
| `graphQL` | `object \| false` | Customize (`name`, `disableQueries`, `disableMutations`) or disable GraphQL. |
| `typescript` | `{ interface: string }` | Override the generated TS interface name. |
| `custom` | `object` | Extension point for plugin metadata. |
| `dbName` | `string` | Custom DB table/collection name. |
| `lockDocuments` | `object \| false` | Document-locking behavior in the admin UI. |
| `forceSelect` | `object` | Fields always returned regardless of `select`. |

## `admin` Options

```ts
// src/globals/Header.ts
admin: {
  group: 'Configuration',                 // Sidebar grouping; `false` hides nav entry
  hidden: false,                          // or (user) => boolean — hides from nav, keeps routes
  description: 'Primary site navigation',
  preview: (doc, { req }) =>              // "Preview" button URL
    `${process.env.NEXT_PUBLIC_SITE_URL}/preview?secret=${process.env.PREVIEW_SECRET}`,
  livePreview: {
    url: ({ data, locale }) =>
      `${process.env.NEXT_PUBLIC_SITE_URL}?draft=true&locale=${locale?.code}`,
    breakpoints: [
      { name: 'mobile', width: 375, height: 667, label: 'Mobile' },
      { name: 'desktop', width: 1440, height: 900, label: 'Desktop' },
    ],
  },
}
```

## Access Control

Globals have **only two** access functions — `read` and `update`. There is no `create` or `delete` (a singleton is created on first save and never deleted). Versioned globals also accept `readVersions`.

```ts
// src/globals/Settings.ts
access: {
  read: () => true,                                   // Public read
  update: ({ req: { user } }) => user?.role === 'admin',
  readVersions: ({ req: { user } }) => Boolean(user), // Versioned globals only
}
```

Each function receives `{ req }` (with the authenticated `req.user`); `update` also receives `data` (the incoming update payload). Return a boolean, or a `where` query constraint to restrict access dynamically. See the `access-control` skill for the full pattern.

## Hooks

Globals expose six lifecycle hooks, each an array of sync or async functions:

| Hook | Fires | Common use |
| --- | --- | --- |
| `beforeOperation` | Before any operation begins | Mutate `args`, side-effects |
| `beforeValidate` | On `update`, before validation | Format/normalize incoming `data` |
| `beforeChange` | After validation, before save | Final data shaping (data is valid) |
| `afterChange` | After the global is saved | Purge caches, sync to CRM, revalidate |
| `beforeRead` | Before `findOne` output transform | Inspect all locales / hidden fields |
| `afterRead` | Last step before returning to client | Computed/derived fields |

```ts
// src/globals/Settings.ts
import type { GlobalConfig } from 'payload'

export const Settings: GlobalConfig = {
  slug: 'settings',
  hooks: {
    afterChange: [
      async ({ doc, previousDoc, req }) => {
        // Revalidate the front-end when settings change
        await fetch(`${process.env.NEXT_PUBLIC_SITE_URL}/api/revalidate?tag=settings`)
        return doc
      },
    ],
  },
  fields: [/* ... */],
}
```

Mutating hooks (`beforeValidate`, `beforeChange`) return the (modified) `data`; read hooks return `doc`. See the `hooks` skill for full argument tables.

## Reading & Writing — Local API

```ts
// Read the global (server-side, fully typed)
const header = await payload.findGlobal({
  slug: 'header',
  depth: 2,          // Resolve relationships 2 levels deep
  locale: 'en',      // Localized field values for this locale
  fallbackLocale: false,
})

// Update the global (data is merged into the single instance)
const updated = await payload.updateGlobal({
  slug: 'settings',
  data: { siteName: 'Acme' },
  depth: 1,
  locale: 'en',
})
```

`findGlobal` also accepts `overrideAccess`, `user`, `showHiddenFields`; `updateGlobal` additionally accepts `overrideLock`. `depth` and `locale` work exactly as they do for collection queries — see the `queries` skill.

## Reading & Writing — REST & GraphQL

```http
GET   /api/globals/header?depth=2&locale=en
POST  /api/globals/settings        # body: JSON of fields to update (REST updates globals via POST)
```

GraphQL auto-generates a query and a mutation per global (e.g. `Header` and `updateHeader`). Customize the GraphQL type name or disable operations via `graphQL: { name, disableQueries, disableMutations }`, or turn it off entirely with `graphQL: false`.

## Versions, Drafts & Localization

Enable version history (and optional drafts) the same way collections do:

```ts
// src/globals/Homepage.ts
versions: {
  drafts: { autosave: { interval: 800 }, schedulePublish: true },
  max: 20,    // Keep the last 20 versions
}
```

A draft-enabled global gains a `_status` field; fetch the working draft with `payload.findGlobal({ slug: 'homepage', draft: true })`. Any field inside a global can be `localized: true`, in which case `findGlobal`/`updateGlobal` resolve values for the requested `locale`. See the `localization` skill for the localized-field model.

## Project Structure

```txt
src/
├── globals/
│   ├── Settings.ts     # Site-wide config
│   ├── Header.ts       # Localized nav
│   └── Footer.ts
├── payload.config.ts
```

Keep each global in its own file; co-locate access functions and hooks in sibling files (`Header.access.ts`, `Header.hooks.ts`) as globals grow.

<example>
**User**: "Build a Header global with localized nav items linking to pages, public read, and admin-only update, grouped under 'Configuration'."

**Solution**:
```ts
// src/globals/Header.ts
import type { GlobalConfig } from 'payload'

export const Header: GlobalConfig = {
  slug: 'header',
  label: 'Header',
  access: {
    read: () => true,
    update: ({ req: { user } }) => user?.role === 'admin',
  },
  admin: {
    group: 'Configuration',
    description: 'Primary site navigation',
  },
  fields: [
    {
      name: 'navItems',
      type: 'array',
      maxRows: 8,
      fields: [
        { name: 'label', type: 'text', required: true, localized: true },
        { name: 'page', type: 'relationship', relationTo: 'pages', required: true },
      ],
    },
  ],
}
```

Register `Header` in `globals: [...]`, then read it on the server:
```ts
const header = await payload.findGlobal({ slug: 'header', depth: 1, locale: 'en' })
```
</example>

<example>
**User**: "Add an afterChange hook to my Settings global that revalidates the cache."

**Solution**:
```ts
// src/globals/Settings.ts
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await fetch(`${process.env.NEXT_PUBLIC_SITE_URL}/api/revalidate?tag=settings`)
      req.payload.logger.info('Settings updated — cache revalidated')
      return doc
    },
  ],
}
```
</example>

## What this skill does NOT cover

- **Field types** inside a global (`text`, `array`, `relationship`, `richText`, etc.) → `fields` skill.
- **Multi-document content types** and the full config → `collections` skill.
- **Access function signatures, `where` constraints, role logic** → `access-control` skill.
- **Hook argument tables and execution order** → `hooks` skill.
- **`depth`, `locale`, `where`, pagination, draft querying** → `queries` skill.
- **Localized fields, locale config, fallbacks** → `localization` skill.

