Docs
Integrating Puck
Feature Toggling

Feature Toggling

Feature toggling is enabled by Puck's Permissions API. This enables you to toggle behavior like:

  • Deletion
  • Dragging
  • Duplication
  • Editing (setting all fields to read-only)
  • etc

See the supported permissions reference for a complete list.

Toggling features globally

Toggling features across the entire Puck instance can be done with global permissions. These can be set by the permissions prop on the Puck component:

export function Editor() {
  return (
    <Puck
      permissions={{
        delete: false, // Disable delete function on all components
      }}
      // ...
    />
  );
}

Toggling features per component

Toggling feature for all instance of a component can be done using component permissions. This is controlled by the permissions parameter on the component config, and inherits the global permissions.

const config = {
  components: {
    HeadingBlock: {
      permissions: {
        delete: false, // Disable delete function on all HeadingBlock instances
      },
      // ...
    },
  },
};

Component permissions can also be applied to the root config.

Toggling features dynamically

Dynamic permissions enable runtime calculation of permissions based on the component data, enabling instance-specific permissions. This is controlled by the resolvePermissions parameter on the component config.

const config = {
  components: {
    HeadingBlock: {
      resolvePermissions: (data, { permissions }) => {
        if (data.props.locked) {
          return {
            delete: false, // Disable delete function when HeadingBlock `locked` prop is set
          };
        }
 
        return permissions; // Return inherited permissions (component or global)
      },
      // ...
    },
  },
};

Asynchronous feature toggling

Permissions can be resolved asynchronously, enabling powerful patterns like querying permissions from an endpoint whenever the data changes.

const config = {
  components: {
    HeadingBlock: {
      resolvePermissions: async (data) => {
        const serverPermissions = await myPermissionsApi(data.props.id); // Query permissions from a server
 
        return serverPermissions;
      },
      // ...
    },
  },
};

Preventing duplicate calls

Permission resolvers are cached based on the component props. If none of the props change, then the resolver won't be called. This prevents duplicate calls to expensive asynchronous operations.

However, it's possible that you may want to avoid making an expensive operation unless a specific prop has changed, rather than any prop.

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

const config = {
  components: {
    HeadingBlock: {
      // ...
      resolvePermissions: async (data, { changed, lastPermissions }) => {
        if (!changed.locked) return lastPermissions; // Return last permissions if `locked` hasn't changed
 
        return await myExpensivePermissionsApi(data),
      },
      // ...
    },
  },
};

Further reading