Skip to main content

Custom Action Widget

A Custom Action widget appears as a button in the bulk-action toolbar on a dashboard or relation widget. When the user selects one or more records and clicks the button, your widget renders inside a toolbar slot.

Use it when you want a custom bulk operation: export, validate, send a notification, or trigger a workflow across multiple selected records.


pageContext shape

Source: BulkActionButtonRenderer.jsx

{
displayName, // The button label configured in Admin
selectedInstances, // Array of currently selected record objects
currentWidgetConfig, // Widget configuration object from Admin

// Common (always present)
settings,
userData,
}

Each item in selectedInstances includes id, name, instanceTypeName, and optionally attributes (if they were included in the view query).


Example 1 — Download selected records as JSON

Read a JSON attribute from each selected record and bundle them into a downloadable file.

function ExportSelectedAsJson(pageContext) {
const { protrakComponents } = React.useContext(customWidgetContext);
const { Button, ButtonEnums, Spinner } = protrakComponents;
const [loading, setLoading] = React.useState(false);

const download = async () => {
setLoading(true);
const data = pageContext.selectedInstances.map((instance) => ({
id: instance.id,
name: instance.name,
}));
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: 'application/json',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'export.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setLoading(false);
};

return (
<Button
onClick={download}
appearance={ButtonEnums.Appearance.TransparentWithHover}
size={ButtonEnums.Size.Large}
title={pageContext.displayName}
>
{loading ? <Spinner /> : <i className="fa fa-download" />}
</Button>
);
}

Example 2 — Confirm and call an API for each selected record

Show a count of selected items and, on click, call an API to process each one.

function MarkAsReviewedBulkAction(pageContext) {
const { protrakUtils, protrakComponents } =
React.useContext(customWidgetContext);
const { protrakApiClient, useAuthContext } = protrakUtils;
const { Button, ButtonEnums, Text } = protrakComponents;
const authContext = useAuthContext();
const [done, setDone] = React.useState(false);

const handleClick = async () => {
const ids = pageContext.selectedInstances.map((i) => i.id);
await Promise.all(
ids.map((id) =>
protrakApiClient(
`instances/${id}/attributes`,
{
method: 'PATCH',
data: [{ name: 'ReviewStatus', textValue: 'Reviewed' }],
},
authContext
)
)
);
setDone(true);
};

if (done)
return (
<Text>Marked {pageContext.selectedInstances.length} as Reviewed.</Text>
);

return (
<Button
onClick={handleClick}
appearance={ButtonEnums.Appearance.Primary}
text={`Mark ${pageContext.selectedInstances.length} as Reviewed`}
/>
);
}

Preview:

CustomAction


Tips

  • selectedInstances may not contain all attribute data. If you need specific attributes, fetch the full instance using protrakApiClient.
  • Always show a loading state while async operations are running.
  • Call pageContext.reload?.() after modifying records to refresh the dashboard.
  • For deep-dive patterns, see Bulk Action Pattern and Utility Action Pattern.