Agent Skillswebiny/webiny-js › webiny-api-cms-custom-field-type

webiny-api-cms-custom-field-type

GitHub

指导如何为Webiny CMS实现自定义字段类型,涵盖扩展DataFieldBuilder、创建工厂类、DI注册及TypeScript模块增强,以支持自定义存储格式和验证逻辑。

skills/user-skills/api/custom-field-type/SKILL.md webiny/webiny-js

Trigger Scenarios

需要Webiny CMS不支持的自定义字段类型 扩展CMS模型构建器API 实现自定义字段验证或存储格式

Install

npx skills add webiny/webiny-js --skill webiny-api-cms-custom-field-type -g -y
More Options

Non-standard path

npx skills add https://github.com/webiny/webiny-js/tree/next/skills/user-skills/api/custom-field-type -g -y

Use without installing

npx skills use webiny/webiny-js@webiny-api-cms-custom-field-type

指定 Agent (Claude Code)

npx skills add webiny/webiny-js --skill webiny-api-cms-custom-field-type -a claude-code -g -y

安装 repo 全部 skill

npx skills add webiny/webiny-js --all -g -y

预览 repo 内 skill

npx skills add webiny/webiny-js --list

SKILL.md

Frontmatter
{
    "name": "webiny-api-cms-custom-field-type",
    "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."
}

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";

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

Complete Example

// extensions/SlugFieldType/SlugFieldType.ts
import { DataFieldBuilder, FieldType } from "webiny/api/cms/model";
import type { FieldTypeValidator } from "webiny/api/cms/model";

// 1. Builder interface — extends DataFieldBuilder + desired FieldTypeValidator types
export interface ISlugFieldBuilder
  extends
    DataFieldBuilder<"slug">,
    FieldTypeValidator.Required,
    FieldTypeValidator.Pattern,
    FieldTypeValidator.Unique {}

// 2. Module augmentation — adds fields.slug() to the registry
declare module "webiny/api/cms/model" {
  interface IFieldBuilderRegistry {
    slug(): ISlugFieldBuilder;
  }

  interface IFieldRendererRegistry {
    myCustomRenderer: {
      fieldType: "text" | "number";
      settings: undefined;
    };
  }
}

// 3. Builder class — implements each validator method via this.validation()
class SlugFieldBuilder extends DataFieldBuilder<"slug"> implements ISlugFieldBuilder {
  constructor() {
    super("slug");
  }

  required(message?: string): this {
    return this.validation({
      name: "required",
      message: message || "Value is required.",
      settings: {}
    });
  }

  pattern(regex: string, flags = "", message?: string): this {
    return this.validation({
      name: "pattern",
      message: message || "Invalid value.",
      settings: { preset: "custom", regex, flags }
    });
  }

  unique(message?: string): this {
    return this.validation({
      name: "unique",
      message: message || "Value must be unique.",
      settings: {}
    });
  }
}

// 4. Factory class — implements FieldType.Factory
class SlugFieldTypeFactory implements FieldType.Factory {
  readonly type = "slug";

  create(): ISlugFieldBuilder {
    return new SlugFieldBuilder();
  }
}

// 5. Export as a FieldType implementation
export const SlugFieldType = FieldType.createImplementation({
  implementation: SlugFieldTypeFactory,
  dependencies: []
});

Using the Custom Field in a Model

After registration, fields.slug() is available in any ModelFactory implementation:

import { ModelFactory } from "webiny/api/cms/model";

class ProductModelImpl implements ModelFactory.Interface {
  async execute(builder: ModelFactory.Builder) {
    return [
      builder
        .public({ modelId: "product", name: "Product", group: "ungrouped" })
        .fields(fields => ({
          name: fields.text().label("Name").required(),
          slug: fields
            .slug()
            .label("Slug")
            .required("Slug is required.")
            .unique()
            .pattern("^[a-z0-9-]+$", "", "Only lowercase letters, numbers, and hyphens.")
        }))
        .layout([["name", "slug"]])
        .titleFieldId("name")
        .singularApiName("Product")
        .pluralApiName("Products")
    ];
  }
}

DataFieldBuilder API

All methods return this for chaining.

Method Description
label(text) Field label shown in the Admin editor
help(text) Help text shown below the field
description(text) Field description
fieldId(id) Override the auto-derived field ID
storageId(id) Override the storage identifier
placeholder(text) Placeholder text for the input
defaultValue(value) Default value for new entries
list() Make the field accept multiple values (array)
listMinLength(n, msg?) Minimum number of list items
listMaxLength(n, msg?) Maximum number of list items
tags(tags) Arbitrary tags for filtering/querying
renderer(name, settings?) Set the Admin UI renderer
settings(settings) Set arbitrary field settings

Protected Methods (for use inside validator implementations only)

Method Description
this.validation(rule) Append a CmsModelFieldValidation to the field's validation array
this.listValidation(rule) Append a CmsModelFieldValidation to the list validation array

A CmsModelFieldValidation has the shape:

{
  name: string; // validator name (e.g., "required", "minLength", "pattern")
  message: string; // error message shown to the user
  settings: Record<string, any>; // validator-specific config
}

Available Validators

Import via import type { FieldTypeValidator } from "webiny/api/cms/model" and extend your builder interface with them. Each type adds one method to your interface:

Type Method signature
FieldTypeValidator.Required required(message?)
FieldTypeValidator.Unique unique(message?)
FieldTypeValidator.MinLength minLength(value, message?)
FieldTypeValidator.MaxLength maxLength(value, message?)
FieldTypeValidator.Pattern pattern(regex, flags?, message?)
FieldTypeValidator.Email email(message?)
FieldTypeValidator.Url url(message?)
FieldTypeValidator.LowerCase lowerCase(message?)
FieldTypeValidator.UpperCase upperCase(message?)
FieldTypeValidator.LowerCaseSpace lowerCaseSpace(message?)
FieldTypeValidator.UpperCaseSpace upperCaseSpace(message?)
FieldTypeValidator.Gte gte(value, message?)
FieldTypeValidator.Lte lte(value, message?)
FieldTypeValidator.DateGte dateGte(value, message?)
FieldTypeValidator.DateLte dateLte(value, message?)
FieldTypeValidator.ListMinLength listMinLength(value, message?)
FieldTypeValidator.ListMaxLength listMaxLength(value, message?)

When implementing a validator method in the builder class, call this.validation() with the appropriate name and settings. For ListMinLength/ListMaxLength, call this.listValidation() instead. The settings shapes:

Validator name settings
Required, Unique "required" / "unique" {}
MinLength, MaxLength "minLength" / "maxLength" { value: String(n) }
Gte, Lte "gte" / "lte" { value: String(n) }
DateGte, DateLte "dateGte" / "dateLte" { value }
Pattern "pattern" { preset: "custom", regex, flags }
Email "pattern" { preset: "email", regex: null, flags: null }
Url "pattern" { preset: "url", regex: null, flags: null }
LowerCase / UpperCase / etc. "pattern" { preset: "lowerCase" / "upperCase" / etc., regex: null, flags: null }

Key Rules

  1. type string must be unique — the factory's readonly type must not collide with any built-in type (text, number, boolean, datetime, file, ref, object, richText, longText, json, dynamicZone) or other custom types.
  2. Module augmentation target — augment "webiny/api/cms/model" using namespace FieldBuilderRegistry { interface Interface { yourType(): IYourFieldBuilder; } }.
  3. validation() is protected — never call it from outside the builder class. Expose validators as named methods on the interface (e.g., required(), minLength()).
  4. dependencies: [] — field type factories have no DI dependencies; always pass an empty array.
  5. Registration order — register custom FieldType implementations before FieldBuilderRegistry is resolved (i.e., in the same register() call or before it runs). The registry collects all FieldType instances at construction time.

Related Skills

  • webiny-api-cms-content-models — Using the model builder's fluent API to define CMS models
  • webiny-api-cms-catalog — Full catalog of CMS abstractions including ModelFactory, FieldType, DataFieldBuilder
  • webiny-dependency-injection — The createImplementation pattern and DI scoping

Version History

  • 80eb1c5 Current 2026-08-20 10:07

Same Skill Collection

.claude/skills/grill-me/SKILL.md
.claude/skills/prd-to-plan/SKILL.md
.claude/skills/preflight/SKILL.md
.claude/skills/tester/SKILL.md
.claude/skills/write-a-prd/SKILL.md
skills/repo-skills/add-feature-flag/SKILL.md
skills/user-skills/admin/admin-architect/SKILL.md
skills/user-skills/admin/admin-permissions/SKILL.md
skills/user-skills/admin/form-model/SKILL.md
skills/user-skills/admin/new-entry-wizard/SKILL.md
skills/user-skills/admin/website-builder/page-settings/SKILL.md
skills/user-skills/admin/website-builder/wb-preview-url-modifier/SKILL.md
skills/user-skills/api-bundle-size-limit/SKILL.md
skills/user-skills/api/api-architect/SKILL.md
skills/user-skills/api/cms-bulk-actions/SKILL.md
skills/user-skills/api/event-handler-pattern/SKILL.md
skills/user-skills/api/graphql-api/SKILL.md
skills/user-skills/api/http-route/SKILL.md
skills/user-skills/api/permissions/SKILL.md
skills/user-skills/api/use-case-pattern/SKILL.md
skills/user-skills/api/v5-to-v6-migration/SKILL.md
skills/user-skills/api/websocket-notifications/SKILL.md
skills/user-skills/cli-extensions/SKILL.md
skills/user-skills/configure-auth0/SKILL.md
skills/user-skills/configure-entraid/SKILL.md
skills/user-skills/configure-okta/SKILL.md
skills/user-skills/dependency-injection/SKILL.md
skills/user-skills/full-stack-architect/SKILL.md
skills/user-skills/generated/api/aco/SKILL.md
skills/user-skills/generated/api/cms/SKILL.md
skills/user-skills/generated/api/file-manager/SKILL.md
skills/user-skills/generated/api/scheduler/SKILL.md
skills/user-skills/generated/api/security/SKILL.md
skills/user-skills/generated/api/system/SKILL.md
skills/user-skills/generated/api/tenancy/SKILL.md
skills/user-skills/generated/api/tenant-manager/SKILL.md
skills/user-skills/generated/api/website-builder/SKILL.md
skills/user-skills/generated/infra/SKILL.md
skills/user-skills/infrastructure-extensions/SKILL.md
skills/user-skills/local-development/SKILL.md
skills/user-skills/mailer-smtp/SKILL.md
skills/user-skills/project-structure/SKILL.md
.claude/skills/webiny-skill-creator/SKILL.md
skills/user-skills/admin/ui-extensions/SKILL.md
skills/user-skills/api/ai-powerups-content/SKILL.md
skills/user-skills/cognito-federation/SKILL.md
skills/user-skills/content-models/SKILL.md
skills/user-skills/generated/admin/aco/SKILL.md
skills/user-skills/generated/admin/ai-powerups/SKILL.md

Metadata

Files
0
Version
80eb1c5
Hash
a1eba8d4
Indexed
2026-08-20 10:07

inicio - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-21 23:32
浙ICP备14020137号-1 $mapa de visitantes$