6.4 KiB
| title | aliases | tags | topic | sources | created | updated | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Payload CMS — TypeScript |
|
|
payloadcms |
|
2026-05-15 | 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.tsfile 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@latestto bootstrap; all templates are TypeScript by default. - Run
payload generate:typesafter every schema change to keeppayload-types.tsin sync. - Export custom reusable types using
interfaceNameon group/array/block/tab fields. - The
@payloadcms/typescript-pluginadds IDE path validation and autocomplete forPayloadComponentstrings — it does NOT affecttscor builds. - Exported types cover: Config, Collections, Globals, Fields, Hooks, and FormState.
Key Steps / Concepts
Generating Types
Run whenever collections/globals change:
payload generate:types
Add an npm script pointing at your config explicitly (needed when config is not at root):
{
"scripts": {
"generate:types": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types"
}
}
Config Options
// 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:
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:
{
type: 'group',
name: 'meta',
interfaceName: 'SharedMeta', // generates top-level interface
fields: [
{ name: 'title', type: 'text' },
{ name: 'description', type: 'text' },
],
}
Generated output:
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
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+Clickon a path string jumps to source
Setup
pnpm add -D @payloadcms/typescript-plugin
// 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:
{
"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:
{
"compilerOptions": {
"plugins": [
{
"name": "@payloadcms/typescript-plugin",
"baseDir": "./src"
}
]
}
}
Common Issues & Fixes
- Types not updating — rerun
payload generate:types; checkPAYLOAD_CONFIG_PATHenv var declareerrors in consuming repo — setdeclare: falseand add manual declaration- Plugin not loading — ensure editor uses workspace TS version, not bundled version
- Naming collision in generated types —
interfaceNameclashes with a collection slug; rename with type suffix
Gotchas
- The TS plugin only runs in the IDE — it does not affect
tscor build output. - Path fields
_h_slugPath/_h_titlePathappear in generated types but areundefinedat runtime unlesscomputeHierarchyPaths: trueis passed. - Copying
payload-types.tsto a frontend repo that does not havepayloadinstalled requiresdeclare: falseto avoid TS errors.