# TypeScript Plugin (Experimental)

Source: https://payloadcms.com/docs/typescript/ts-plugin

> **Warning**
>
> **Experimental** — This plugin is experimental and may change in future
> releases. Please [report any
> issues](https://github.com/payloadcms/payload/issues) you encounter.

Payload ships an optional TypeScript Language Service Plugin (`@payloadcms/typescript-plugin`) that enhances your IDE experience when working with [Custom Components](/docs/v3/custom-components/overview.md). It understands Payload's `PayloadComponent` import path conventions and provides:

- **Path validation** — red squigglies when a component path doesn't resolve to a real file
- **Export validation** — errors when the named export doesn't exist in the target module, with "Did you mean?" suggestions
- **Autocomplete** — file and directory suggestions while typing the path, and export name suggestions after `#`
- **Go-to-definition** — Ctrl/Cmd+click on a component path string to jump to the component's source

## Installation

Install the plugin as a dev dependency:

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

Then add it to the `plugins` array in your `tsconfig.json`:

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

### VS Code / Cursor Setup

TypeScript language service plugins only load when the editor uses the **workspace version** of TypeScript (the one installed in your project's `node_modules`). By default, VS Code uses its own bundled version which won't load the plugin.

To switch: open the command palette (`Cmd+Shift+P`) and run **"TypeScript: Select TypeScript Version"**, then choose **"Use Workspace Version"**.

For teams, add the following to `.vscode/settings.json` so everyone is prompted to switch:

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

After selecting the workspace version, restart the TypeScript server (`Cmd+Shift+P` → "TypeScript: Restart TS Server").

> **Note**
>
> TypeScript Language Service Plugins only run inside your IDE. They do not
> affect `tsc` compilation or your build output.

## Supported Path Conventions

The plugin supports all of Payload's component path formats:

| Format                  | Example                            | Resolution                     |
| ----------------------- | ---------------------------------- | ------------------------------ |
| Absolute (from baseDir) | `'/components/MyField#MyField'`    | Resolved relative to `baseDir` |
| Relative                | `'./components/MyField#MyField'`   | Resolved relative to `baseDir` |
| tsconfig alias          | `'@/components/MyField#MyField'`   | Resolved via tsconfig `paths`  |
| Package import          | `'@payloadcms/ui/rsc#MyComponent'` | Resolved via node_modules      |
| Default export          | `'/components/MyField'`            | Uses `default` export          |

Both the **string form** and the **object form** are supported:

```ts
// String form
{
  admin: {
    components: {
      Field: '/components/MyField#MyField',
    }
  }
}

// Object form
{
  admin: {
    components: {
      Field: {
        path: '/components/MyField',
        exportName: 'MyField',
      }
    }
  }
}
```

## Configuration

### Base Directory

The plugin automatically detects `baseDir` by walking up from the current file to find the nearest `payload.config.ts`. Absolute paths (starting with `/`) and relative paths (starting with `./`) are resolved relative to this directory.

If your project uses a non-standard layout, you can override `baseDir` in the plugin config:

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

The `baseDir` path is relative to the `tsconfig.json` location.

## How It Works

The plugin detects `PayloadComponent` positions by checking the contextual type of string literals in your config. Any string typed as `PayloadComponent`, `CustomComponent`, or any type that resolves to the same `false | RawPayloadComponent | string` union shape is automatically validated.

This means the plugin works everywhere Payload expects a component path — collection field components, global components, admin panel components, dashboard widgets, and custom views — without needing to hardcode specific config positions.
