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

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

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