Skip to content

Events

The component communicates with your page exclusively through DOM custom events. All of them bubble and cross the shadow boundary (bubbles: true, composed: true), so you can listen on the element, a container, or document. Payloads ride in event.detail.

document.querySelector('forms-engine')
.addEventListener('fe-completed', (e) => {
console.log('response id:', e.detail.responseId);
});
Event When detail
fe-loaded The questionnaire rendered (fresh load or resume) { publicId, versionNumber } — plus refStatus: "NONE" | "COMPLETED" when an external-ref was supplied on a fresh load
fe-resumed Right after fe-loaded when a refreshed session picked up where it left off { responseId, screenId, stepId, tabId } — screenId is "stepId::tabId", null if no screen is visible
fe-screen-changed Every navigation — Next, Back, step/tab jumps, and rule-driven moves when the current screen becomes hidden { stepId, tabId }
fe-completed Successful completion, before any completion-redirect navigation { responseId }
fe-already-submitted The supplied reference has already completed this questionnaire (detected at mount, or racing another session) — fired instead of fe-completed { externalRef }
fe-error Load, save, or completion failures; configuration problems; unknown question types; upload failures Two shapes, below

In the editor’s preview mode (and when you drive the component with a definition object yourself), fe-loaded carries { preview: true } and a preview Finish emits fe-completed with { responseId: null, preview: true }.

Failures of a network operation carry the underlying cause:

{ message: "Saving answers failed", cause: /* the thrown error */ }
// messages: "Failed to load questionnaire" | "Saving answers failed"
// | "Completing the questionnaire failed"

Conditions the component detected itself carry a code instead:

{ code: "EXTERNAL_REF_REQUIRED", message: "…" } // one-per-ref policy, no external-ref given
{ code: "UNKNOWN_QUESTION_TYPE", type: "…", message: "…" } // once per unknown type
{ code: "FILE_UPLOAD_FAILED", message: "…" } // an upload failed (respondent sees inline retry)

Check detail.code !== undefined to tell them apart. None of these are fatal by themselves — the component always renders something (an error state, a placeholder, a retry banner) rather than disappearing.

  • fe-completed always fires before a completion-redirect navigates away, so completion handlers (analytics, unlocking the next page step) reliably run.
  • On resume, fe-loaded fires before fe-resumed.
  • When a submission is rejected as a duplicate (one-per-reference race), fe-completed is not emitted — only fe-already-submitted.