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