Fields define how content gets displayed and edited. Pages CMS comes with a few built-in fields, but you can also add your own custom fields.
All fields are saved in the src/fields
folder:
fields/
├─ core/
│ ├─ boolean/
│ ├─ code/
│ ├─ date/
│ ├─ image/
│ ├─ number/
│ ├─ ...
├─ custom/
│ ├─ README.md
├─ registry.js
core/
is where all the built-in fields are saved (e.g. boolean, code, date). The names of the fields subfolders match the types used in the configuration file (e.g. type: boolean
).
custom/
(empty by default) is used to host the subfolders for custom fields.
registry.js
is a helper function to create registries for the fields.
Each field folder (e.g. fields/core/boolean
) should include an index.ts
or index.tsx
file that can export:
schema
: a Zod schema used to process the field when saving. This is used for validation and format coertion.supportsList
: a boolean defining whether or not the field handles lists internally rather than using the default list behavior. Check fields/core/image
for an example.read
: a function used to convert the field value when reading from a file (e.g. fields/core/date
will convert from the format
defined for that date field to a standard ISO 8601 date/datetime).write
: a function used to convert the field value when writing to a file (e.g. fields/core/date
will convert from a standard ISO 8601 date/datetime to the format
defined for that date field).EditComponent
: a React component used to edit the field.ViewComponent
: a React component used to display the field in a collection.defaultValue
: defines the default value for the field.fields/core/boolean
Let's have a look at the fields/core/boolean
folder, which defines boolean fields:
fields/
├─ core/
│ ├─ boolean/
│ │ ├─ edit-component.tsx
│ │ ├─ index.tsx
│ │ ├─ view-component.tsx
edit-component.tsx
is the React component used in the editor (for both creating or editing content). It is called by components/entry/entry-form.tsx
via the editComponents
registry.
In the case of the boolean field, it is a simple toggle switch:
"use client";
import { forwardRef } from "react";
import { Switch } from "@/components/ui/switch";
const EditComponent = forwardRef((props: any, ref: React.Ref<HTMLInputElement>) => {
return (
<div>
<Switch
{...props}
ref={ref}
checked={props.value}
onCheckedChange={props.onChange}
/>
</div>
);
});
export { EditComponent };
view-component.tsx
is the React component used to display the field value in collections. It is called by components/collection/collection-view.tsx
via the viewComponents
registry.
For boolean fields, we displayed a colored chip with the labels "True" or "False" if the value is defined, or nothing otherwise (undefined
or null
):
"use client";
const ViewComponent = ({ value }: { value: boolean}) => {
return (
<>
{value == null
? null
: value
? <span className="inline-block rounded-md bg-primary text-primary-foreground px-2 py-0.5 text-xs font-medium">True</span>
: <span className="inline-block rounded-md border bg-muted px-2 py-0.5 text-xs font-medium">False</span>
}
</>
);
};
export { ViewComponent };
index.tsx
registers the field. It is required for your field to work.
For the boolean field, this means exporting the edit/view components, the default value and the schema:
import { z } from "zod";
import { Field } from "@/types/field";
import { EditComponent } from "./edit-component";
import { ViewComponent } from "./view-component";
const defaultValue = false;
const schema = (field: Field) => {
let zodSchema = z.coerce.boolean();
return zodSchema;
};
export { EditComponent, ViewComponent, defaultValue, schema };
Notice that we define a default value (false
) and coerce the field to a boolean in the schema function. This will ensure we're saving a boolean in the file, and not a string.
In some cases (e.g. the image field), we may also indicate support for lists with supportsList
, and define read
and write
functions to convert the input and output values (when reading from or writing to the file).