Building your own custom fields in Payload is as easy as writing a React component. Any field type can be extended further to make your own custom field, right down to how it works in the admin panel. In this way, you can avoid reinventing everything about a field and only work on adding your custom business logic exactly how you need.
To demonstrate this, we're going to create a simple color picker component for use right in the Payload CMS admin panel. By the end of this guide, we'll have created a modular, reusable custom field that can be dropped into any Payload CMS app with ease.
The component will:
text
field typeuseFieldType
hookPreferences
featureCell
component, showing the selected color in the List
view of the admin panelAll the code written for this guide can be seen in the Custom Field Guide repository.
You can use your own Payload app or start a new one for this guide. If you haven't started a project yet, you can get started easily by running npx create-payload-app
in your terminal.
For more details on how to start an application, including how to do so from scratch, read the installation documentation.
The first step is to create a new file in your app for our new field's config. That will let us import it to different collections wherever it is needed. Because we want our field to store a string in the database, just like the built-in text
field type does, we'll set our field's type
equal to text
. That will tell Payload how to handle storing the data. We'll also write a simple validation function to tell the backend and frontend what to allow to be saved.
We'll import the field to an existing collection, so we can see it in use, before building it up a bit more.
/src/collections/ToDoLists.ts
:
This is a good time to mention that because we're just dealing with JavaScript, you could import this field and use it anywhere. You could also change individual properties specific to this collection by destructuring the object and add extra properties you wish to set. To do that, in place of the imported colorField
instead do { ...colorField, required: false }
, or any other properties as needed.
So far, the default Text
component is still rendering in the admin panel. Let's swap that out with a custom component, and modify the field's config to include it.
Custom field components are just basic React components, so let's scaffold that out and then build the extra features one-by-one. Create a new file for the Field
component:
/src/color-picker/InputField.tsx
:
You'll see above that Payload automatically provides our React component with the props
that it needs. The most important prop
is the path
, which we pass on to the useFieldType
hook. This hook allows us to set the field's value and have it work with the rest of the Payload form.
The component returns the markup for the component, complete with a Label and a list of clickable colors.
This won't be very functional until we add styling. Let's add a new line to import a new stylesheet: import './styles.scss';
. Create that file and paste in the following SCSS:
/src/color-picker/styles.scss
:
The simple styles above will give the color "chips" a clickable circle to set the value and show which is currently selected.
Another part of the custom component that we can add is a nice way to display the color right in a collection List
. There, we can create the following:
/src/color-picker/Cell.tsx
:
Note that we can reuse our styles here as we want the color "chip" to appear the same. We get the cellData from the Prop and that will be our saved hex values for the field.
Now that we have a functional component to serve as our input, we can update color-picker/config.ts
with a new admin
property:
Now is a good time to see it working! After you login and navigate to the url to create a new Todo item you will see the component and can use it to create a new Todo list
Back in the List
view, you should also be able to see the color that was chosen right in the table. If you don't see the color column, expand the column list to include it.
What we have is nice if we want to control available color options closely, but we know our users want to add their own too. Let's add to the UI a way to do that and while we're at it we should store the user's newly added colors in Payload's user preferences to re-use color options without re-entering them every time.
To make the interactions possible, we'll add more state variables and useEffect
hooks. We also need to import and use the validation logic from the config, and set the value in a new Input which we can import styles directly from Payload to make it look right.
By adding Payload's usePreferences()
hook, we can get and set user specific data relevant to the color picker all persisted in the database without needing to write new endpoints. You will see we call setPreference()
and getPreference()
to get and set the array of color options specific to the authenticated user.
Note that the preferenceKey should be something completely unique across your app to avoid overwriting other preference data.
Now, for the complete component code:
/src/color-picker/InputField.tsx
:
We made a lot of changes—hopefully the code speaks for itself. Everything we did adds to the interactivity and usability of the field.
Lastly we want to finish off the styles of our input with a few new pieces.
Update your styles.scss
with the following:
/src/color-picker/styles.scss
:
The custom color picker in this guide serves as an example of one way you could extend the UI to create a better authoring experience for users.
I hope you're inspired to create your own fantastic UI components using Payload CMS. Feel free to share what you build in the GitHub discussions.