Skills

Webiny API Cms Custom Field Type

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

Install

npx skills add webiny/webiny-js --skill webiny-api-cms-custom-field-type -g -a claude-code -y
Repository
webiny/webiny-js8.1k
Category
Engineering
Updated
5d ago
License
See repository
Context cost
Mediumabout 2,850 tokens
Runs scripts
No, instructions only

What it does

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.

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:

// 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:

// api/Extension.ts
import { createFeature } from "webiny/api";
import { SlugFieldTypeFeature } from "~/extensions/SlugFieldType/feature.js";
Read the whole SKILL.md on GitHub

More in Engineering