Custom Actions
Custom actions let you add any business logic to a form's action bar. You register a handler function with a unique type key. The form schema references that key. When the user clicks the button, the handler receives a rich FormActionContext and does whatever your application needs.
FormActionContext Interface
Your custom handler receives this context object:
interface FormActionContext {
// Current form state
formId: string;
tenantId: string;
formValues: Record<string, any>;
formEngine: FormEngine;
// Application services
apiClient: FormDefinitionApiClient;
// Navigation
navigate: (url: string) => void;
// Completion signals
complete: () => void; // Signal success — hides loading state
fail: (reason?: string) => void; // Signal failure — shows error, hides loading
// Action config from schema
config?: Record<string, any>;
// Validation
validate: () => Promise<boolean>;
}
Registering a Custom Action
import { registerFormAction } from '@atlas-forms/control-registry-js';
registerFormAction('export-to-pdf', async (context: FormActionContext) => {
try {
const { formValues, formId, tenantId } = context;
// Validate first if needed
const isValid = await context.validate();
if (!isValid) {
context.fail('Please fix validation errors before exporting.');
return;
}
// Call your custom service
const pdfBlob = await pdfService.generatePDF(formId, formValues, tenantId);
downloadBlob(pdfBlob, `form-${formId}.pdf`);
context.complete(); // Signal success
} catch (err) {
context.fail('PDF export failed. Please try again.');
}
});
Using in the Form Schema
{
"type": "custom",
"label": "Export PDF",
"variant": "secondary",
"icon": "file-pdf",
"config": {
"handlerType": "export-to-pdf",
"handlerConfig": {
"template": "detailed",
"includeSignature": true
}
}
}
The handlerConfig is available in your handler as context.config, allowing the same handler to behave differently based on the form that uses it.
Handler Lifecycle
When the user clicks a custom action button:
- Button enters loading state (spinner visible)
- Your async handler is called with
FormActionContext - If you call
context.complete()— button exits loading, success toast shown - If you call
context.fail(reason)— button exits loading, error toast shown - If your handler throws — treated as
fail()with generic error message
Accessing Other Services
Custom action handlers can import any service from your application. The context.apiClient gives you the Atlas Forms API client. For other services (your own APIs, EdgeStream, workflow engine), import them directly in the handler file.