Dynamic Props

Dynamic prop resolution allows you to change the props for a component after the props have been changed by the user. This is useful for making third-party API calls, such as requesting the latest content from a headless CMS.

Dynamic component props

The resolveData function allows you to make changes to the props and set fields as read-only.

For example, we can set the value of one prop to another:

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

When inserting components with resolveData, the Puck state will update twice - once for the initial insert, and once more when the method resolves, if it changes the data. This will be reflected in the undo/redo history.

Setting fields as read-only

resolveData also allows us to mark fields as read-only using the readOnly parameter.

const config = {
  components: {
    HeadingBlock: {
      // ...
      resolveData: async ({ props }) => {
        return {
          props: {
            resolvedTitle: props.title,
          },
          readOnly: { resolvedTitle: true },
        };
      },
      // ...
    },
  },
};
Interactive Demo
The resolvedTitle field is locked

Preventing duplicate calls

It’s possible that resolveData may carry out an expensive operation (like an API call) that we want to avoid making unless a specific prop has changed.

This can be restricted by checking the changed param before calling any expensive operations.

const config = {
  components: {
    HeadingBlock: {
      // ...
      resolveData: async ({ props }, { changed }) => {
        if (!changed.text) return { props };
 
        return {
          props: {
            resolvedTitle: await expensiveOperation(props.title),
          },
        };
      },
      // ...
    },
  },
};

Dynamic Root props

The resolveData method is also available on the root component.

const config = {
  components: {},
  root: {
    fields: {
      title: {
        type: "text",
      },
      resolvedTitle: {
        type: "text",
      },
    },
    resolveData: async ({ props }) => {
      return {
        props: {
          resolvedTitle: props.title,
        },
      };
    },
    render: ({ children, resolvedTitle }) => {
      return (
        <>
          <h1>{resolvedTitle}</h1>
          {children}
        </>
      );
    },
  },
};

Triggering resolveData

Resolve data is triggered whenever the props for a component change, or when the resolveAllData utility is used.

import { resolveAllData } from "@measured/puck";
 
const updatedData = await resolveAllData(data, config);

Further reading