Callbacks
Callbacks let you hook into editor lifecycle events and intercept user actions. Pass them via the callbacks property in your configuration.
Configuration
const config: PexelizeConfig = {
callbacks: {
onReady: () => { /* ... */ },
onLoad: (design) => { /* ... */ },
onChange: (design) => { /* ... */ },
onError: (error) => { /* ... */ },
onModuleSave: async (data) => { /* ... */ },
onPreview: (data) => { /* ... */ },
onContentDialog: async (data) => { /* ... */ },
onHeaderRowClick: () => { /* ... */ },
onFooterRowClick: () => { /* ... */ },
onLockedRowClick: (data) => { /* ... */ },
},
};
Callback Reference
onReady
Type: () => void
Blocking: No
Fires when the editor is fully initialized and ready for interaction.
onReady: () => {
console.log('Editor is ready');
enableSaveButton();
},
onLoad
Type: (design: DesignJson) => void
Blocking: No
Fires when a design is loaded into the editor (via loadDesign() or the initial design prop).
onLoad: (design) => {
console.log('Design loaded', design.body.rows.length, 'rows');
},
onChange
Type: (design: DesignJson) => void
Blocking: No
Fires on every design modification. Use for auto-save or dirty-state tracking.
onChange: (design) => {
setIsDirty(true);
debouncedSave(design);
},
onChange fires frequently. Debounce any expensive operations (network requests, large state updates) to avoid performance issues.
onError
Type: (error: EditorError) => void
Blocking: No
Fires when the editor encounters an error.
onError: (error) => {
console.error('Editor error:', error.message, error.code);
reportToSentry(error);
},
onModuleSave
Type: (data: ModuleSaveData) => Promise<void>
Blocking: Yes
Fires when the user saves a reusable module (content block). The editor waits for the returned promise to resolve before completing the save.
interface ModuleSaveData {
moduleId: string;
name: string;
design: DesignJson;
thumbnail?: string; // base64 PNG
}
onModuleSave: async (data) => {
await fetch('/api/modules', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
},
onPreview
Type: (data: PreviewData) => void
Blocking: No
Fires when the user clicks the preview button. Use to open a custom preview instead of the built-in modal.
interface PreviewData {
html: string;
design: DesignJson;
}
onPreview: (data) => {
openCustomPreviewModal(data.html);
},
onContentDialog
Type: (data: ContentDialogData) => Promise<DesignJson>
Blocking: Yes
Fires when the user opens a content dialog (e.g., template picker, saved content browser). Return a DesignJson to insert into the editor.
interface ContentDialogData {
type: 'template' | 'module' | 'content';
}
onContentDialog: async (data) => {
const selectedTemplate = await openTemplatePicker(data.type);
return selectedTemplate.design;
},
onHeaderRowClick
Type: () => void
Blocking: No
Fires when the user clicks the locked header row. Use to show a settings dialog for the header.
onHeaderRowClick: () => {
openHeaderSettings();
},
onFooterRowClick
Type: () => void
Blocking: No
Fires when the user clicks the locked footer row.
onFooterRowClick: () => {
openFooterSettings();
},
onLockedRowClick
Type: (data: { rowId: string }) => void
Blocking: No
Fires when the user clicks any locked row (other than header/footer).
onLockedRowClick: (data) => {
console.log('Locked row clicked:', data.rowId);
openRowSettings(data.rowId);
},
Blocking vs Fire-and-Forget
| Callback | Blocking |
|---|---|
onReady | No |
onLoad | No |
onChange | No |
onError | No |
onModuleSave | Yes — editor waits for the promise |
onPreview | No |
onContentDialog | Yes — editor waits for the returned design |
onHeaderRowClick | No |
onFooterRowClick | No |
onLockedRowClick | No |
Blocking callbacks must return a Promise. The editor shows a loading indicator while waiting. If the promise rejects, the operation is cancelled and a toast notification is shown.
For AI-related callbacks (onSmartTextGenerate, onImageGenerate, etc.), see the AI section.