Docs
Extending Puck
Custom Fields

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".

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>
          ),
        },
      },
      // ...
    },
  },
};
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>;
      },
    },
  },
};

Further reading