Skip to main content

Custom fields

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

The present documentation is intended for custom field creators: it describes which APIs and functions developers must use to create a new custom field. The User Guide describes how to add and use custom fields from Strapi's admin panel.

It is recommended that you develop a dedicated plugin for custom fields. Custom field plugins include both a server and admin panel part. The custom field must be registered in both parts before it is usable in Strapi's admin panel.

Once created and used, custom fields are defined like any other attribute in the model's schema. An attribute using a custom field will have its type represented as customField (i.e. type: 'customField'). Depending on the custom field being used a few additional properties may be present in the attribute's definition (see models documentation).

NOTES
  • Though the recommended way to add a custom field is through creating a plugin, app-specific custom fields can also be registered within the global register function found in src/index.js and src/admin/app/js files.
  • Custom fields can only be shared using plugins.

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. This method is used 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:

ParameterDescriptionType
nameThe name of the custom fieldString
plugin(optional)The name of the plugin creating the custom fields❗️ If defined, the pluginId value on the admin panel registration must have the same value (see Registering a custom field in the admin panel)String
typeThe data type the custom field will useString
inputSize(optional)Parameters to define the width of a custom field's input in the admin panelObject

The optional inputSize object, when specified, must contain all of the following parameters:

ParameterDescriptionType
defaultThe default size in columns that the input field will occupy in the 12-column grid in the admin panel.The value can either be 4, 6, 8 or 12.Integer
isResizableWhether the input can be resized or notBoolean
Current limitations

Currently:

  • Custom fields cannot add new data types to Strapi and must use existing, built-in Strapi data types described in the models' attributes documentation.
  • You also cannot modify an existing data type.
  • Special data types unique to Strapi, such as relation, media, component, or dynamic zone data types, cannot be used in custom fields.

Example: Registering an example "color" custom field on the server:

In the following example, the color-picker plugin was created using the CLI generator (see plugins development):

./src/plugins/color-picker/server/register.js
"use strict";

module.exports = ({ strapi }) {
strapi.customFields.register({
name: "color",
plugin: "color-picker",
type: "string",
inputSize: {
// optional
default: 4,
isResizable: true,
},
});
};

The custom field could also be declared directly within the strapi-server.js file if you didn't have the plugin code scaffolded by the CLI generator:

./src/plugins/color-picker/strapi-server.js
module.exports = {
register({ strapi }) {
strapi.customFields.register({
name: "color",
plugin: "color-picker",
type: "text",
inputSize: {
// optional
default: 4,
isResizable: true,
},
});
},
};

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. This method is used to register custom fields in the admin panel during the plugin's admin register 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:

ParameterDescriptionType
nameName of the custom fieldString
pluginId(optional)Name of the plugin creating the custom field❗️ If defined, the plugin value on the server registration must have the same value (see Registering a custom field on the server)String
typeExisting Strapi data type the custom field will use❗️ Relations, media, components, or dynamic zones cannot be used.String
icon(optional)Icon for the custom fieldReact.ComponentType
intlLabelTranslation for the nameIntlObject
intlDescriptionTranslation for the descriptionIntlObject
componentsComponents needed to display the custom field in the Content Manager (see components)
options(optional)Options to be used by the Content-type Builder (see options)Object

Example: Registering an example "color" custom field in the admin panel:

In the following example, the color-picker plugin was created using the CLI generator (see plugins development):

./src/plugins/color-picker/admin/src/index.js
import ColorPickerIcon from "./components/ColorPicker/ColorPickerIcon";

export default {
register(app) {
// ... app.addMenuLink() goes here
// ... app.registerPlugin() goes here

app.customFields.register({
name: "color",
pluginId: "color-picker", // the custom field is created by a color-picker plugin
type: "string", // the color will be stored as a string
intlLabel: {
id: "color-picker.color.label",
defaultMessage: "Color",
},
intlDescription: {
id: "color-picker.color.description",
defaultMessage: "Select any color",
},
icon: ColorPickerIcon, // don't forget to create/import your icon component
components: {
Input: async ()
import(
/* webpackChunkName: "input-component" */ "./components/Input"
),
},
options: {
// declare options here
},
});
},

// ... bootstrap() goes here
};

Components

app.customFields.register() must pass a components object with an Input React component to use in the Content Manager's edit view.

Example: Registering an Input component

In the following example, the color-picker plugin was created using the CLI generator (see plugins development):

./src/plugins/color-picker/admin/src/index.js
export default {
register(app) {
app.customFields.register({
// …
components: {
Input: async ()
import(/* webpackChunkName: "input-component" */ "./Input"),
},
// …
});
},
};

Custom field input components receive the following props:

PropDescriptionType
attributeThe attribute object with custom field's underlying Strapi type and options{ type: String, customField: String }
descriptionThe field description set in configure the viewIntlObject
placeholderThe field placeholder set in configure the viewIntlObject
hintThe field description set in configure the view along with min/max validation requirementsString
nameThe field name set in the content-type builderString
intlLabelThe field name set in the content-type builder or configure the viewIntlObject
onChangeThe handler for the input change event. The name argument references the field name. The type argument references the underlying Strapi type({ target: { name: String value: unknown type: String } }) void
contentTypeUIDThe content-type the field belongs toString
typeThe custom field uid, for example plugin::color-picker.colorString
valueThe input value the underlying Strapi type expectsunknown
requiredWhether or not the field is requiredboolean
errorError received after validationIntlObject
disabledWhether or not the input is disabledboolean

As of Strapi v4.13.0, fields in the Content Manager can be auto-focussed via the URLSearchParam field. It's recommended that your input component is wrapped in React's forwardRef method; you should pass the corresponding ref to the input element.

Example: A custom text input

In the following example we're providing a custom text input that is controlled. All inputs should be controlled otherwise their data will not be submitted on save.

./src/plugins/<plugin-name>/admin/src/components/Input.js
import * as React from "react";

import { useIntl } from "react-intl";

const Input = React.forwardRef((props, ref) {
const { attribute, disabled, intlLabel, name, onChange, required, value } =
props; // these are just some of the props passed by the content-manager

const { formatMessage } = useIntl();

const handleChange = (e) {
onChange({
target: { name, type: attribute.type, value: e.currentTarget.value },
});
};

return (
<label>
{formatMessage(intlLabel)}
<input
ref={ref}
name={name}
disabled={disabled}
value={value}
required={required}
onChange={handleChange}
/>
</label>
);
});

export default Input;

For a more detailed view of the props provided to the customFields and how they can be used check out the ColorPickerInput file in the Strapi codebase. :::

Options

app.customFields.register() can pass an additional options object with the following parameters:

Options parameterDescriptionType
baseSettings available in the Base settings tab of the field in the Content-type BuilderObject or Array of Objects
advancedSettings available in the Advanced settings tab of the field in the Content-type BuilderObject or Array of Objects
validatorValidator function returning an object, used to sanitize input. Uses a yup schema object.Function

Both base and advanced settings accept an object or an array of objects, each object being a settings section. Each settings section could include:

  • a sectionTitle to declare the title of the section as an IntlObject
  • and a list of items as an array of objects.

Each object in the items array can contain the following parameters:

Items parameterDescriptionType
nameLabel of the input.Must use the options.settingName format.String
descriptionDescription of the input to use in the Content-type BuilderIntlObject
intlLabelTranslation for the label of the inputIntlObject
typeType of the input (e.g., select, checkbox)String

Example: Declaring options for an example "color" custom field:

In the following example, the color-picker plugin was created using the CLI generator (see plugins development):

./src/plugins/color-picker/admin/src/index.js
// imports go here (ColorPickerIcon, pluginId, yup package…)

export default {
register(app) {
// ... app.addMenuLink() goes here
// ... app.registerPlugin() goes here
app.customFields.register({
// …
options: {
base: [
/*
Declare settings to be added to the "Base settings" section
of the field in the Content-Type Builder
*/
{
sectionTitle: {
// Add a "Format" settings section
id: "color-picker.color.section.format",
defaultMessage: "Format",
},
items: [
// Add settings items to the section
{
/*
Add a "Color format" dropdown
to choose between 2 different format options
for the color value: hexadecimal or RGBA
*/
intlLabel: {
id: "color-picker.color.format.label",
defaultMessage: "Color format",
},
name: "options.format",
type: "select",
value: "hex", // option selected by default
options: [
// List all available "Color format" options
{
key: "hex",
defaultValue: "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: [
/*
Declare settings to be added to the "Advanced settings" section
of the field in the Content-Type Builder
*/
],
validator: (args) ({
format: yup.string().required({
id: "options.color-picker.format.error",
defaultMessage: "The color format is required",
}),
}),
},
});
},
};

The Strapi codebase gives an example of how settings objects can be described: check the baseForm.ts file for the base settings and the advancedForm.js file for the advanced settings. The base form lists the settings items inline but the advanced form gets the items from an attributeOptions.js file. :::