Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
eaf5b96
WIP
pwizla Aug 2, 2022
5b96d6d
Merge branch 'dev/custom-fields' into dev/custom-fields--development
pwizla Aug 2, 2022
42f46ff
Add WIP reference and example guide for custom fields
pwizla Aug 2, 2022
80ec7bd
Delete old file, now splitted into reference and guide
pwizla Aug 2, 2022
7ffda04
Improve reference guide
pwizla Aug 3, 2022
41e89f5
Add first draft of color picker custom fields guide
pwizla Aug 3, 2022
7743d80
Improve reference documentation
pwizla Aug 3, 2022
05c5a14
Improve example guide
pwizla Aug 3, 2022
851e8b6
Mention custom fields in plugins development
pwizla Aug 3, 2022
b4e0f6d
Fix TOC casing
pwizla Aug 3, 2022
683a47e
Merge branch 'dev/custom-fields' into dev/custom-fields--development
pwizla Aug 3, 2022
00fb768
Remove tutorial and update sidebar and "main" doc file name
pwizla Aug 8, 2022
2634f19
Temporarily remove callout for tutorial
pwizla Aug 8, 2022
dc6d7eb
Improve custom fields reference documentation
pwizla Aug 8, 2022
3cfb3aa
Further improve custom fields reference documentation
pwizla Aug 9, 2022
1289d5a
Update first note with a mention to app-specific custom fields
pwizla Aug 9, 2022
60d508c
Add prerequisites (plugin)
pwizla Aug 9, 2022
18d3068
Update `items` table
pwizla Aug 9, 2022
86c4baf
Add multimarkdown support + prepare for documenting all settings fields
pwizla Aug 9, 2022
9dd518d
Improve introduction
pwizla Aug 9, 2022
1964792
Improve `plugin` key description
pwizla Aug 9, 2022
4c00f4c
Delete deprecated TODOs
pwizla Aug 9, 2022
cc6fb87
Fix paths in example code
pwizla Aug 9, 2022
387604a
Update wording: settings → options
pwizla Aug 9, 2022
7feb893
Improve `options` table
pwizla Aug 9, 2022
b0027fc
Update prerequisites to also mention enabling plugin
pwizla Aug 9, 2022
458ede8
Merge branch 'dev/custom-fields' into dev/custom-fields--development
pwizla Aug 10, 2022
5aae19b
Remove callout from plugins dev, will be moved to another PR
pwizla Aug 10, 2022
cc6ea34
Reword introduction sentence
pwizla Aug 16, 2022
848ae5a
Reword models attributes intro. description
pwizla Aug 16, 2022
f76cc3e
Fix preposition and singular usage
pwizla Aug 16, 2022
cccb2f9
Update text → string in color picker example
pwizla Aug 16, 2022
e18818d
Mention `media` can't be used as a data type
pwizla Aug 16, 2022
ce35b44
Add `media` to the list of forbidden data types
pwizla Aug 16, 2022
7eb15ae
Update validator code example
pwizla Aug 16, 2022
ff08676
Remove question comment about inline components
pwizla Aug 17, 2022
cf17672
Update string → text
pwizla Aug 17, 2022
30b2250
Remove View component
pwizla Aug 17, 2022
3d09675
Finish removing View component and update "Components" section
pwizla Aug 17, 2022
f4cd19e
Improve mention of non-usable data types
pwizla Aug 17, 2022
39f6c5b
Remove bootstrap() and mention src/admin/app.s
pwizla Aug 17, 2022
ff5a07d
Add link to user guide + mention CF can be added to components
pwizla Aug 17, 2022
6d64d0a
Remove some comments
pwizla Aug 17, 2022
f2add84
Replace bootstrap() with register()
pwizla Aug 17, 2022
3204dc3
Mention yup
pwizla Aug 17, 2022
b613344
Improve settings items description
pwizla Aug 17, 2022
f858e89
Remove note about not being able to import custom input components
pwizla Aug 17, 2022
03c4f91
Improve `options` description and code example
pwizla Aug 17, 2022
95a261a
Fix user guide link (no #)
pwizla Aug 18, 2022
88278f6
Update schema.json → schema
pwizla Aug 18, 2022
c95cc07
Remove non-relevant TODO
pwizla Aug 18, 2022
14b15ae
Update strapi code base links
pwizla Aug 18, 2022
c7b627a
Don't mention what happens if `plugin` is omitted in server
pwizla Aug 18, 2022
f1211a3
Simplify callouts
pwizla Aug 18, 2022
ef76391
Fix CTB spelling
pwizla Aug 22, 2022
639f19f
Move note about forbidden data types into the table
pwizla Aug 23, 2022
29889d6
Fix 'can not' → 'cannot'
pwizla Aug 23, 2022
ad6d0a8
Fix: can't → cannot
pwizla Aug 23, 2022
50ddf7e
Add tip for `Input` component props
pwizla Aug 23, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/.vuepress/config/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@ const markdown = {
extendMarkdown: md => {
// use more markdown-it plugins!
md.use(require('markdown-it-include'));
md.use(require('markdown-it-multimd-table'), {
Copy link
Collaborator Author

@pwizla pwizla Aug 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we won't need this. It's mostly if we need to document complex tables (see this commit) which adds support for MultiMarkdown tables.

multiline: true,
rowspan: true,
headerless: true
});
},
};
4 changes: 1 addition & 3 deletions docs/.vuepress/config/sidebar-developer.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,7 @@ const developer = [
['/developer-docs/latest/development/admin-customization', 'Admin panel customization'],
['/developer-docs/latest/development/plugins-extension.md', 'Plugins extension'],
['/developer-docs/latest/development/plugins-development.md', 'Plugins development'],
{
title: 'Custom Fields',
},
['/developer-docs/latest/development/custom-fields.md', 'Custom fields'],
['/developer-docs/latest/development/typescript.md', 'TypeScript'],
['/developer-docs/latest/development/providers.md', 'Providers'],
],
Expand Down
340 changes: 340 additions & 0 deletions docs/developer-docs/latest/development/custom-fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
---
title: Custom fields reference - Strapi Developer Docs
description: Learn how you can use custom fields to extend Strapi's content-types capabilities.
sidebarDepth: 3
canonicalUrl: https://docs.strapi.io/developer-docs/latest/development/custom-fields.html
---

# Custom fields

Custom fields extend Strapi’s capabilities by adding new types of fields to content-types and components. Once created or installed, 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](/user-docs/latest/plugins/introduction-to-plugins.md#custom-fields) describes how to install and use custom fields from Strapi's admin panel.

<!-- TODO: uncomment and adjust content when blog post is ready -->
<!-- ::: strapi Prefer to learn by building?
If you'd rather directly jump to a concrete example, see our [Creating a color custom field guide](#) page for step-by-step instructions on how to build your first custom field from scratch.
::: -->

It is recommended that you develop a dedicated [plugin](/developer-docs/latest/development/plugins-development.md) 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](/developer-docs/latest/development/backend-customization/models.md#custom-fields)).

::: note 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](/developer-docs/latest/setup-deployment-guides/configurations/optional/functions.md) 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

::: prerequisites
!!!include(developer-docs/latest/development/snippets/custom-field-requires-plugin.md)!!!
:::

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](/developer-docs/latest/developer-resources/plugin-api-reference/server.md#register).

`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`<br/><br/>(_optional_) | The name of the plugin creating the custom fields | `String` |
| `type` | The data type the custom field will use | `String` |

::: note
Currently, custom fields cannot add new data types to Strapi and must use existing, built-in Strapi data types described in the [models' attributes](/developer-docs/latest/development/backend-customization/models.md#model-attributes) documentation. Special data types unique to Strapi, such as relation, media, component, or dynamic zone data types, cannot be used in custom fields.
:::

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

```js
// path: ./src/plugins/my-custom-field-plugin/strapi-server.js

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

:::

## Registering a custom field in the admin panel

::: prerequisites
!!!include(developer-docs/latest/development/snippets/custom-field-requires-plugin.md)!!!
:::

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](/developer-docs/latest/developer-resources/plugin-api-reference/admin-panel.md#register).

`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` | Name of the custom field | `String` |
| `pluginId`<br/><br/>(_optional_) | Name of the plugin creating the custom field | `String` |
| `type` | Existing Strapi data type the custom field will use<br/><br/>❗️ Relations, media, components, or dynamic zones cannot be used. | `String` |
| `icon`<br/><br/>(_optional_) | Icon for the custom field | `React.ComponentType` |
| `intlLabel` | Translation for the name | [`IntlObject`](https://formatjs.io/docs/react-intl/) |
| `intlDescription` | Translation for the description | [`IntlObject`](https://formatjs.io/docs/react-intl/) |
| `components` | Components needed to display the custom field in the Content Manager (see [components](#components)) |
| `options`<br/><br/>(_optional_) | Options to be used by the Content-type Builder (see [options](#options)) | `Object` |

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

```jsx
// path: ./src/plugins/color-picker/strapi-admin.js

register(app) {
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: ColorIcon,
components: {
Input: async () => import(/* webpackChunkName: "input-component" */ "./Input"),
}
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',
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',
}),
})
}),
},
});
}
```

:::

### Components

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

::: details Example: Registering an Input component

```js
// path: ./src/plugins/my-custom-field-plugin/strapi-admin.js

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

:::

::: tip
The `Input` React component receives several props. The [`ColorPickerInput` file](https://github.com/strapi/strapi/blob/features/custom-fields/examples/getstarted/src/plugins/mycustomfields/admin/src/components/ColorPicker/ColorPickerInput/index.js#L10-L21) in the Strapi codebase gives you an example of how they can be used.
:::


### Options

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

| Options parameter | Description | Type |
| -------------- | ------------------------------------------------------------------------------- | ----------------------- |
| `base` | Settings available in the _Base settings_ tab of the field in the Content-type Builder | `Object` or `Array of Objects` |
| `advanced` | Settings available in the _Advanced settings_ tab of the field in the Content-type Builder | `Object` or `Array of Objects` |
| `validator` | Validator function returning an object, used to sanitize input. Uses a [`yup` schema object](https://github.com/jquense/yup/tree/pre-v1). | `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`](https://formatjs.io/docs/react-intl/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically a sectionTitle is not required, or it can be set to null. For example you might to want to append an input option to the existing section on the base form. You can see an example of that on my branch

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case the options property has an array of objects (it's the same as the items array)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, Mark! I've updated the docs and replaced the code example by copying/pasting what was in your branch (see this commit. Please let me know what you think 🙂

- and a list of `items` as an array of objects.

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

| Items parameter | Description | Type |
| --------------- | ------------------------------------------------------------------ | ---------------------------------------------------- |
| `name` | Label of the input.<br/>Must use the `options.settingName` format. | `String` |
| `description` | Description of the input to use in the Content-type Builder | `String` |
| `intlLabel` | Translation for the label of the input | [`IntlObject`](https://formatjs.io/docs/react-intl/) |
| `type` | Type of the input (e.g., `select`, `checkbox`) | `String` |



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

```jsx
// path: ./src/plugins/my-custom-field-plugin/strapi-admin.js

register(app) {
app.customFields.register({
// …
options: {
base: [
{
intlLabel: {
id: 'color-picker.color.format.label',
defaultMessage: 'Color format',
},
name: 'options.format',
type: 'select',
value: 'hex',
options: [
{
key: '__null_reset_value__',
value: '',
metadatas: {
intlLabel: {
id: 'color-picker.color.format.placeholder',
defaultMessage: 'Select a format',
},
hidden: true,
},
},
{
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: [
{
sectionTitle: {
id: 'global.settings',
defaultMessage: 'Settings',
},
items: [
{
name: 'required',
type: 'checkbox',
intlLabel: {
id: 'form.attribute.item.requiredField',
defaultMessage: 'Required field',
},
description: {
id: 'form.attribute.item.requiredField.description',
defaultMessage: "You won't be able to create an entry if this field is empty",
},
},
{
name: 'private',
type: 'checkbox',
intlLabel: {
id: 'form.attribute.item.privateField',
defaultMessage: 'Private field',
},
description: {
id: 'form.attribute.item.privateField.description',
defaultMessage: 'This field will not show up in the API response',
},
},
],
},
],
validator: args => ({
format: yup.string().required({
id: 'options.color-picker.format.error',
defaultMessage: 'The color format is required',
}),
}),
},
});
}
```

:::

<!-- TODO: replace these tip and links by proper documentation of all the possible shapes and parameters for `options` -->
::: tip
The Strapi codebase gives an example of how settings objects can be described: check the [`baseForm.js`](https://github.com/strapi/strapi/blob/main/packages/core/content-type-builder/admin/src/components/FormModal/attributes/baseForm.js) file for the `base` settings and the [`advancedForm.js`](https://github.com/strapi/strapi/blob/main/packages/core/content-type-builder/admin/src/components/FormModal/attributes/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`](https://github.com/strapi/strapi/blob/main/packages/core/content-type-builder/admin/src/components/FormModal/attributes/attributeOptions.js) file.
:::
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Registering a custom field through a plugin requires creating and enabling a plugin (see [Plugins development](/developer-docs/latest/development/plugins-development.md#create-a-plugin)).
2 changes: 2 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"dependencies": {
"@vuepress/plugin-html-redirect": "^0.1.4",
"@vuepress/plugin-medium-zoom": "^1.8.2",
"markdown-it": "^13.0.1",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment at the top of the PR. Only required to support MultiMarkdown tables if we need some.

"markdown-it-include": "^2.0.0",
"markdown-it-multimd-table": "^4.2.0",
"vuepress": "^1.8.2",
"vuepress-plugin-code-copy": "^1.0.6",
"vuepress-plugin-element-tabs": "^0.2.8",
Expand Down
Loading