# Webiny API Cms Custom Field Type

How to implement a custom CMS field type that integrates with the model builder's...

- Repository: https://github.com/webiny/webiny-js (8051 stars)
- Category: Engineering
- License: see repository
- Last updated: 2026-10-30
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add webiny/webiny-js --skill webiny-api-cms-custom-field-type -g -a claude-code -y`
Codex: `npx skills add webiny/webiny-js --skill webiny-api-cms-custom-field-type -g -a codex -y`
Cursor: `npx skills add webiny/webiny-js --skill webiny-api-cms-custom-field-type -g -a cursor -y`
Gemini CLI: `npx skills add webiny/webiny-js --skill webiny-api-cms-custom-field-type -g -a gemini-cli -y`

## Description

How to implement a custom CMS field type that integrates with the model builder's fluent API. Covers extending DataFieldBuilder, composing validator interfaces, creating a FieldTypeFactory, registering via DI, and module augmentation for TypeScript autocomplete on the fields() registry.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# Custom CMS Field Type

## TL;DR

A custom field type is a class that extends `DataFieldBuilder<"yourType">`, paired with a factory class implementing `FieldType.Factory`. Register the factory with `container.register(YourFieldType)`. Add a module augmentation on `"webiny/api/cms/model"` so the `fields` registry method gets TypeScript autocomplete.

## When to Use This

Use a custom field type when:

- You need a field with a storage format or validation logic not covered by the built-in types (`text`, `number`, `boolean`, `datetime`, `file`, `ref`, `object`, `richText`, `longText`, `json`, `dynamicZone`)
- You want to expose a fluent builder API (e.g., `fields.slug()`, `fields.color()`) in `ModelFactory` implementations

## Field Type Structure

A custom field type consists of three parts:

1. **Builder interface**, extends `DataFieldBuilder<"type">` plus `FieldTypeValidator.*` types
2. **Builder class**, implements the interface, calls `this.validation()` for each validator
3. **Factory class**, implements `FieldType.Factory`, creates builder instances

As a standalone extension (not part of a larger feature), the directory layout is:

```
extensions/
└── SlugFieldType/
    ├── SlugFieldType.ts     # builder interface, builder class, factory class
    └── feature.ts           # createFeature, registers the factory into the DI container
```

`feature.ts`:

```ts
// extensions/SlugFieldType/feature.ts
import { createFeature } from "webiny/api";
import { SlugFieldType } from "./SlugFieldType.js";

export const SlugFieldTypeFeature = createFeature({
  name: "SlugFieldType",
  register(container) {
    container.register(SlugFieldType);
  }
});
```

Register in the API entry point:

```ts
// api/Extension.ts
import { createFeature } from "webiny/api";
import { SlugFieldTypeFeature } from "~/extensions/SlugFieldType/feature.js";

The rest: https://github.com/webiny/webiny-js/blob/HEAD/skills/user-skills/api/custom-field-type/SKILL.md
