---
title: "Payload CMS — TypeScript"
aliases: [payload-typescript, payloadcms-typescript]
tags: [payloadcms, typescript, tech-patterns]
topic: payloadcms
sources:
  - raw/typescript__overview.md
  - https://payloadcms.com/docs/typescript/overview
  - https://payloadcms.com/docs/typescript/generating-types
  - https://payloadcms.com/docs/typescript/ts-plugin
created: 2026-05-15
updated: 2026-05-15
---

# PayloadCMS — TypeScript

## Overview

- Payload is built entirely in TypeScript — native first-class support, no bolt-on.
- Scaffold a new project: `npx create-payload-app@latest` → pick a TypeScript template.
- The generated `payload-types.ts` file wires up automatic type inference across the entire Local API.

## Key Takeaways

- Payload is TypeScript-native — all internals, config, hooks, and access control are fully typed.
- Use `npx create-payload-app@latest` to bootstrap; all templates are TypeScript by default.
- Run `payload generate:types` after every schema change to keep `payload-types.ts` in sync.
- Export custom reusable types using `interfaceName` on group/array/block/tab fields.
- The `@payloadcms/typescript-plugin` adds IDE path validation and autocomplete for `PayloadComponent` strings — it does NOT affect `tsc` or builds.
- Exported types cover: Config, Collections, Globals, Fields, Hooks, and FormState.

## Key Steps / Concepts

### Generating Types

Run whenever collections/globals change:

```bash
payload generate:types
```

Add an npm script pointing at your config explicitly (needed when config is not at root):

```json
{
  "scripts": {
    "generate:types": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types"
  }
}
```

### Config Options

```ts
// payload.config.ts
{
  typescript: {
    // Default: path.resolve(__dirname, './payload-types.ts')
    outputFile: path.resolve(__dirname, './generated-types.ts'),

    // Disable auto-declare (for cross-repo type sharing)
    declare: false,

    // Extend the JSON schema → extend generated types
    schema: [
      ({ jsonSchema }) => {
        jsonSchema.definitions.MyType = {
          type: 'object',
          properties: { title: { type: 'string' } },
          required: ['title'],
        }
        return jsonSchema
      },
    ],
  },
}
```

If you disable `declare`, add the declaration manually:

```ts
import { Config } from './payload-types'
declare module 'payload' {
  export interface GeneratedTypes extends Config {}
}
```

### Custom Field Interfaces (`interfaceName`)

Hoist reusable field types with `interfaceName` on `array`, `block`, `group`, and named `tab` fields:

```ts
{
  type: 'group',
  name: 'meta',
  interfaceName: 'SharedMeta',  // generates top-level interface
  fields: [
    { name: 'title', type: 'text' },
    { name: 'description', type: 'text' },
  ],
}
```

Generated output:

```ts
export interface SharedMeta {
  title?: string
  description?: string
}

export interface Post {
  meta?: SharedMeta
}
```

**Warning:** `interfaceName` values are top-level — naming collisions with collection slugs will break generation. Suffix with field type (e.g. `MetaGroup`).

### External Schema References

```ts
typescript: {
  schema: [
    ({ jsonSchema }) => {
      jsonSchema.definitions.MyType = {
        $ref: './schemas/my-type.json',  // resolved from process.cwd()
      }
      return jsonSchema
    },
  ],
}
```

### Exported Types — Reference

| Category | Types |
|----------|-------|
| Config | `Config`, `SanitizedConfig`, `ClientConfig` |
| Collections | `CollectionConfig`, `CollectionSlug` |
| Globals | `GlobalConfig` |
| Fields | `Field`, `TextField`, `RelationshipField`, etc. |
| Hooks | `CollectionBeforeChangeHook`, `GlobalAfterReadHook`, etc. |
| Form state | `FormState` (renamed from `Fields` in v3) |

## TypeScript Language Service Plugin (Experimental)

Plugin `@payloadcms/typescript-plugin` adds IDE intelligence for `PayloadComponent` path strings.

### Features

- Path validation — red squiggles for non-existent component paths
- Export validation — catches missing named exports, with "Did you mean?" suggestions
- Autocomplete — file/dir completions after `/`, export completions after `#`
- Go-to-definition — `Cmd+Click` on a path string jumps to source

### Setup

```bash
pnpm add -D @payloadcms/typescript-plugin
```

```json
// tsconfig.json
{
  "compilerOptions": {
    "plugins": [
      { "name": "next" },
      { "name": "@payloadcms/typescript-plugin" }
    ]
  }
}
```

**VS Code / Cursor** — must use workspace TypeScript, not bundled:
- `Cmd+Shift+P` → "TypeScript: Select TypeScript Version" → "Use Workspace Version"
- Then: "TypeScript: Restart TS Server"

Add to `.vscode/settings.json` for the whole team:

```json
{
  "js/ts.tsdk.path": "node_modules/typescript/lib",
  "js/ts.tsdk.promptToUseWorkspaceVersion": true
}
```

### Supported Path Formats

| Format | Example |
|--------|---------|
| Absolute (from `baseDir`) | `'/components/MyField#MyField'` |
| Relative | `'./components/MyField#MyField'` |
| tsconfig alias | `'@/components/MyField#MyField'` |
| Package import | `'@payloadcms/ui/rsc#MyComponent'` |
| Default export | `'/components/MyField'` |

Both string form and object form (`{ path, exportName }`) are validated.

### baseDir Override

Plugin auto-detects `baseDir` by walking up to find `payload.config.ts`. Override when using non-standard layouts:

```json
{
  "compilerOptions": {
    "plugins": [
      {
        "name": "@payloadcms/typescript-plugin",
        "baseDir": "./src"
      }
    ]
  }
}
```

## Common Issues & Fixes

- **Types not updating** — rerun `payload generate:types`; check `PAYLOAD_CONFIG_PATH` env var
- **`declare` errors in consuming repo** — set `declare: false` and add manual declaration
- **Plugin not loading** — ensure editor uses workspace TS version, not bundled version
- **Naming collision in generated types** — `interfaceName` clashes with a collection slug; rename with type suffix

## Gotchas

- The TS plugin only runs in the IDE — it does not affect `tsc` or build output.
- Path fields `_h_slugPath` / `_h_titlePath` appear in generated types but are `undefined` at runtime unless `computeHierarchyPaths: true` is passed.
- Copying `payload-types.ts` to a frontend repo that does not have `payload` installed requires `declare: false` to avoid TS errors.

## Related

- [[wiki/payloadcms/getting-started|Getting Started]]
- [[wiki/payloadcms/configuration|Configuration]]
- [[wiki/payloadcms/local-api|Local API]]
