Custom Fields
Puck can be extended with completely custom fields for different use-cases.
Creating a custom field
Creating a custom field is possible using the custom
field type:
const config = {
components: {
Example: {
fields: {
title: {
type: "custom",
render: ({ name, onChange, value }) => (
<input
defaultValue={value}
name={name}
onChange={(e) => onChange(e.currentTarget.value)}
style={{ border: "1px solid black", padding: 4 }}
/>
),
},
},
render: ({ title }) => {
return <p>{title}</p>;
},
},
},
};
The onChange
function updates the Puck data payload for the field name, in this case "title".
Interactive Demo
Example
Hello, world
Adding a label
You can add your own label, but it's recommended to use the <FieldLabel>
component provided by Puck to seamlessly integrate into the Puck field UI.
import { FieldLabel } from "@measured/puck";
const config = {
components: {
Example: {
fields: {
title: {
type: "custom",
label: "Label Example",
render: ({ field }) => (
<FieldLabel label={field.label}>
<input {/*...*/} />
</FieldLabel>
),
},
},
// ...
},
},
};
Interactive Demo
Example
Hello, world
Rendering Puck fields internally
Use the <AutoField>
component to render Puck fields within your custom field.
import { AutoField } from "@measured/puck";
const config = {
components: {
Example: {
fields: {
title: {
type: "custom",
label: "Label Example",
render: ({ field, value, onChange }) => (
<FieldLabel label={field.label}>
<AutoField
field={{ type: "text" }}
onChange={(value) => onChange(value)}
value={value}
/>
</FieldLabel>
),
},
// ...
},
},
},
};
Interactive Demo
Example
Hello, world
Updating the UI state
The onChange
function can also be used to modify the Puck UI state at the same time as updating the field value:
const config = {
components: {
Example: {
fields: {
title: {
type: "custom",
render: ({ name, onChange, value }) => (
<input
defaultValue={value}
name={name}
onChange={(e) =>
onChange(
e.currentTarget.value,
// Close the left side bar when this field is changed
{ leftSideBarVisible: false }
)
}
style={{ border: "1px solid black", padding: 4 }}
/>
),
},
},
render: ({ title }) => {
return <p>{title}</p>;
},
},
},
};