# Custom fields

Custom fields extend Strapi’s capabilities by adding new types of fields to content-types. Once created or installed, custom fields can be used in the Content-Types Builder and Content Manager just like built-in fields.

The present documentation is intended to custom fields creators: it describes how custom fields work and can be created from a developer point of view, using dedicated APIs. The user guide describes how to install and use custom fields from Strapi's admin panel.

Custom fields are a specific type of Strapi plugins that include both a back-end (or server) part and a front-end (or admin panel) part. Both parts should be registered separately before a custom field is usable in Strapi's admin panel:

  • strapi.customFields.register registers the server part of a custom field in strapi-server.js
  • app.customFields.register registers the admin panel part of a custom field in strapi-admin.js

✏️ NOTE

Once registered, custom fields can be used in models's schema.json. Custom fields' attributes should declare their type as customField and use the customField property to mention the registered custom field to use (see model's custom fields documentation).

# Registering a custom field on the server

Strapi's server needs to be aware of all the custom fields to ensure that an attribute using a custom field is valid.

The strapi.customFields object exposes a register() method on the Strapi instance to register custom fields on the server during the plugin's server register lifecycle.

strapi.customFields.register() registers one or several custom field(s) on the server by passing an object (or an array of objects) with the following parameters:

Parameter Description Type
name The name of the custom field String
plugin

(optional)
The name of the plugin creating the custom fields

If omitted, the custom field is registered within the global namespace.
String
type The data type the custom field will use String

✋ CAUTION

Currently custom fields can not add new data types to Strapi and should use existing, built-in Strapi data types (e.g. string, number, JSON — see models documentation for the full list).

Example: Registering an example "color" custom field on the server:
// path: ./src/plugins/my-custom-field-plugin/strapi-server.js

module.exports = {
  register({ strapi }) {
    strapi.customFields.register({
      name: 'color',
      plugin: 'color-picker', // the custom 
      type: 'text',
    });
  },
};

# Registering a custom field in the admin panel

Custom fields must be registered in Strapi's admin panel to be available in the Content-Type Builder and the Content Manager.

The app.customFields object exposes a register() method on the StrapiApp instance to register custom fields in the admin panel during the plugin's admin bootstrap lifecycle.

app.customFields.register() registers one or several custom field(s) in the admin panel by passing an object (or an array of objects) with the following parameters:

Parameter Description Type
name The name of the custom field String
pluginId

(optional)
The name of the plugin creating the custom field

If omitted, the custom field is registered within the global namespace.
String
type The existing Strapi data type the custom field will use String
icon

(optional)
The icon for the custom field React.ComponentType
intlLabel The translation for the name IntlObject
intlDescription The translation for the description IntlObject
components The components needed to display the custom field in the Content Manager (see components)
options

(optional)
The settings to extend in the Content-Type Builder (see options) Object

✏️ NOTE

Relations, components or dynamic zones can't be used as a custom field's type parameter.

Example: Registering an example "color" custom field in the admin panel:
// path: ./src/plugins/my-custom-field-plugin/strapi-server.js

register(app) {
  app.customFields.register({
    name: "color",
    pluginId: "color-picker", // the custom field is created by a color-picker plugin
    type: "text", // store the color as a text
    intlLabel: {
      id: "color-picker.color.label",
      defaultMessage: "Color",
    },
    intlDescription: {
      id: "color-picker.color.description",
      defaultMessage: "Select any color",
    } 
    icon: ColorIcon,
    components: {
      Input: async () => import(/* webpackChunkName: "input-component" */ "./Input"),
      View: async () => import(/* webpackChunkName: "view-component" */ "./View"),
    } 
    options: {
      base: [
        {
          sectionTitle: {
            id: 'color-picker.color.section.format',
            defaultMessage: 'Format',
          },
          items: [
            {
              intlLabel: {
                id: 'color-picker.color.format.label',
                defaultMessage: 'Color format',
              },
              name: 'options.format',
              type: 'select',
              value: 'hex',
              options: [
                {
                  key: 'hex',
                  value: 'hex',
                  metadatas: {
                    intlLabel: {
                      id: 'color-picker.color.format.hex',
                      defaultMessage: 'Hexadecimal',
                    },
                  },
                },
                {
                  key: 'rgba',
                  value: 'rgba',
                  metadatas: {
                    intlLabel: {
                      id: 'color-picker.color.format.rgba',
                      defaultMessage: 'RGBA',
                    },
                  },
                },
              ],
            },
          ],
        },
      ],
      advanced: [],
      validator: (args) => ({
        'color-picker': yup.object().shape({
          format: yup.string().oneOf(['hex', 'rgba']),
        }),
      }),
    },
  });
}

# Components

app.customFields.register() must pass a components object with 1 or 2 of the following components:

Component Description
Input React component to use in the Content Manager's edit view
View Read-only React component to use in the Content Manager's list view
Example: Registering Input and View components imported from dedicated files:
// path: ./src/plugins/my-custom-field-plugin/strapi-server.js

register(app) {
  app.customFields.register({
    // …
    components: {
      Input: async () => import(/* webpackChunkName: "input-component" */ "./Input"),
      View: async () => import(/* webpackChunkName: "view-component" */ "./View"),
    } 
    // …
  });
}

# Options

app.customFields.register() can pass additional options to be used for the base and advanced settings tabs of the Content-Type Builder. The options object accepts the following parameters:

Options parameter Description Type
base Settings available in the Base settings tab of the Content-Type Builder Object or Object[]
advanced Settings available in the Advanced settings tab of the Content-Type Builder Object or Object[]
validator Validator function returning an object (useful to sanitize input, for instance) Function

Both base and advanced settings accept:

  • a sectionTitle (as an React IntlObject)
  • and a list of items as an array of objects, each object containing the following parameters:
Items parameter Description Type
intlLabel Label for the setting item, in the React Intl (opens new window) format Object
name Name of the setting to be used, in the following format: options.<setting-name> String
type ? String
value ? String
metadatas ? Object

✏️ NOTE

When extending a custom field’s base and advanced forms in the Content-type Builder, it is not currently possible to import custom input components.

Example: Declaring settings for an example "color" custom field:
// path: ./src/plugins/my-custom-field-plugin/strapi-server.js

register(app) {
  app.customFields.register({
    // …
    options: {
      base: [
        {
          sectionTitle: {
            id: 'color-picker.color.section.format',
            defaultMessage: 'Format',
          },
          items: [
            {
              intlLabel: {
                id: 'color-picker.color.format.label',
                defaultMessage: 'Color format',
              },
              name: 'options.format',
              type: 'select',
              value: 'hex',
              options: [
                {
                  key: 'hex',
                  value: 'hex',
                  metadatas: {
                    intlLabel: {
                      id: 'color-picker.color.format.hex',
                      defaultMessage: 'Hexadecimal',
                    },
                  },
                },
                {
                  key: 'rgba',
                  value: 'rgba',
                  metadatas: {
                    intlLabel: {
                      id: 'color-picker.color.format.rgba',
                      defaultMessage: 'RGBA',
                    },
                  },
                },
              ],
            },
          ],
        },
      ],
      advanced: [],
      validator: (args) => ({
        'color-picker': yup.object().shape({
          format: yup.string().oneOf(['hex', 'rgba']),
        }),
      }),
    },
  });
}