ComponentConfig

The configuration for each component defined in Config.

const config = {
  components: {
    HeadingBlock: {
      fields: {
        title: {
          type: "text",
        },
      },
      render: ({ title }) => <h1>{title}</h1>,
    },
  },
};

Params

ParamExampleTypeStatus
render()render: () => <div />FunctionRequired
fieldsfields: { title: { type: "text"} }Object-
defaultPropsdefaultProps: { title: "Hello, world" }Object-
inlineinline: trueBoolean-
labellabel: "Heading Block"String-
permissions()permissions: { delete: false }Object-
resolveData()resolveData: async ({ props }) => ({ props })Object-
resolveFields()resolveFields: async ({ props }) => ({})}Object-
resolvePermissions()resolvePermissions: async ({ props }) => ({})}Object-

Required params

render(props)

A render function to render your component. Receives props as defined in fields, and some internal Puck props.

const config = {
  components: {
    HeadingBlock: {
      render: () => <h1>Hello, world</h1>,
    },
  },
};

Render props

ArgExampleType
idbutton-1234String
puck.dragRefnullFunction
puck.isEditingfalseBoolean
puck.renderDropZone() => {}Function
...props{}Object
id

A unique ID generated by Puck for this component. You can optionally apply this, or use your own ID.

puck.dragRef

A ref that tells Puck which element is draggable. Apply this to your components when using the inline parameter for advanced CSS layouts.

const config = {
  components: {
    Example: {
      inline: true,
      render: ({ puck: { dragRef } }) => {
        return <div ref={dragRef}>Hello, world</div>;
      },
    },
  },
};
puck.isEditing

A boolean describing whether or not this component is being rendered in the <Puck> component.

puck.renderDropZone

A render method that creates a <DropZone> for creating nested components. Use this method instead of the <DropZone> when implementing React server components.

const config = {
  components: {
    Example: {
      render: ({ puck: { renderDropZone } }) => {
        return <div>{renderDropZone({ zone: "my-content" })}</div>;
      },
    },
  },
};
...props

The remaining props are provided by the user-defined fields.

Optional params

fields

An object describing which Field to show for each prop passed to the component.

const config = {
  components: {
    HeadingBlock: {
      fields: {
        title: {
          type: "text",
        },
      },
      render: ({ title }) => <h1>{title}</h1>,
    },
  },
};
Interactive Demo
Example

Hello, world

defaultProps

Default props to apply to a new instance of the component.

const config = {
  components: {
    HeadingBlock: {
      fields: {
        title: {
          type: "text",
        },
      },
      defaultProps: { title: "Hello, world" },
      render: ({ title }) => <h1>{title}</h1>,
    },
  },
};
Interactive Demo
Example

Hello, world

inline

Render your component without a wrapping element. Use this to create advanced CSS layouts. Defaults to false.

When true, you must to specify which item is draggable via the puck.dragRef prop.

const config = {
  components: {
    HeadingBlock: {
      inline: true,
      render: ({ puck }) => <h1 ref={puck.dragRef}>Hello, World</h1>,
    },
  },
};
Interactive Demo
Example

Hello, world

label

A label to show when referring to your component within the Puck editor. Defaults to the key of your component.

const config = {
  components: {
    HeadingBlock: {
      label: "Heading Block",
      render: () => <h1>Hello, World</h1>,
    },
  },
};
Interactive Demo
Heading Block

permissions

Set the permissions for all instances of a component to toggle functionality. Inherits global permissions.

const config = {
  components: {
    HeadingBlock: {
      permissions: {
        delete: false, // Disable deletion of all HeadingBlock instances
      },
      render: () => <h1>Hello, World</h1>,
    },
  },
};

resolveData(data, params)

Dynamically change the props and set fields as read-only. Supports asynchronous calls.

This function is triggered when <Puck> renders, when a field is changed, or when the resolveAllData function is called.

Example mapping 'title' to 'resolvedTitle'
const config = {
  components: {
    HeadingBlock: {
      fields: {
        title: {
          type: "text",
        },
      },
      resolveData: async ({ props }) => {
        return {
          props: { resolvedTitle: props.title },
          readOnly: { resolvedTitle: true },
        };
      },
      render: ({ resolvedTitle }) => <h1>{resolvedTitle}</h1>,
    },
  },
};
Interactive Demo
Try changing the "title" field

Args

PropExampleType
data{ props: { title: "Hello, world" }, readOnly: {} }Object
params{ changed: { title: true } }Object
data.props

The current props for the component.

const resolveData = async ({ props }) => {
  return {
    props: { resolvedTitle: props.title },
  };
};
data.readOnly

The fields currently set to read-only for this component.

params.changed

An object describing which props have changed on this component since the last time resolveData was called.

Example only updating 'resolvedTitle' when 'title' changes
const resolveData = async ({ props }, { changed }) => {
  if (!changed.title) {
    return { props };
  }
 
  return {
    props: { resolvedTitle: props.title },
  };
};
params.lastData

The data object from the previous run of this function.

Returns

PropExampleType
data{ props: { title: "Hello, world" }, readOnly: {} }Object
data.props

A partial props object containing modified props. Will be spread into the other props.

Example only updating resolvedTitle when title changes
const resolveData = async ({ props }) => {
  return {
    props: { resolvedTitle: props.title },
  };
};
data.readOnly

A partial object describing fields to set as readonly. Will be spread into the existing readOnly state.

Example only updating resolvedTitle when title changes
const resolveData = async ({ props }) => {
  return {
    props: { resolvedTitle: props.title },
    readOnly: { resolvedTitle: true }, // Make the `resolvedTitle` field read-only
  };
};

resolveFields(data, params)

Dynamically set the fields for this component. Supports asynchronous calls.

This function is triggered when the component data changes.

Example changing one field based on another
const config = {
  components: {
    MyComponent: {
      resolveFields: (data) => {
        const fields = {
          drink: {
            type: "radio",
            options: [
              { label: "Water", value: "water" },
              { label: "Orange juice", value: "orange-juice" },
            ],
          },
        };
 
        if (data.props.drink === "water") {
          return {
            ...fields,
            waterType: {
              // ... Define field
            },
          };
        }
 
        return fields;
      },
      // ...
    },
  },
};
Interactive Demo
Try changing the "drink" field

water (still)

Args

PropExampleType
data{ props: { title: "Hello, world" }, readOnly: {} }Object
params{ appState: {}, changed: {}, fields: {}, lastData: {}, lastFields: {}, parent: {} }Object
data.props

The current props for the selected component.

data.readOnly

The fields currently set to read-only for this component.

params.appState

An object describing the AppState.

params.changed

An object describing which props have changed on this component since the last time this function was called.

Example only updating the fields when 'fieldType' changes
const resolveFields = async ({ props }, { changed, lastFields }) => {
  if (!changed.fieldType) {
    return lastFields;
  }
 
  return {
    title: {
      type: fieldType,
    },
  };
};
params.fields

The static fields for this component as defined by fields.

params.lastData

The data object from the previous run of this function.

params.lastFields

The last fields object created by the previous run of this function.

params.parent

The parent data object if this item is within a DropZone.

resolvePermissions(data, params)

Dynamically set the permissions for this component to toggle functionality. Can be used to control the permissions on a specific component instance. Inherits permissions. Supports asynchronous calls.

This function is triggered when the component data changes.

Setting permissions for a specific instance
const config = {
  components: {
    MyComponent: {
      resolvePermissions: (data, { permissions }) => {
        if (data.props.id === "MyComponent-1234") {
          return {
            delete: false, // Disable deletion on component with id MyComponent-1234
          };
        }
 
        return { permissions }; // Fallback to inherited permissions
      },
      // ...
    },
  },
};

Args

PropExampleType
data{ props: { title: "Hello, world" }, readOnly: {} }Object
params{ appState: {}, changed: {}, permissions: {}, lastData: {}, lastPermissions: {} }Object
data.props

The current props for the selected component.

data.readOnly

The fields currently set to read-only for this component.

params.appState

An object describing the AppState.

params.changed

An object describing which props have changed on this component since the last time this function was called. This helps prevent duplicate calls when making async operations.

Example only updating the permissions when 'example' prop changes
const resolveFields = async ({ props }, { changed, lastPermissions }) => {
  if (!changed.example) {
    return lastPermissions; // Return the last permissions unless the `example` prop has changed
  }
 
  return await expensiveAsyncOperation();
};
params.permissions

The static fields for this component as defined by permissions.

params.lastData

The data object from the previous run of this function.

params.lastPermissions

The last permissions object created by the previous run of this function.

Returns

A fields object.