JavaScript API
Flow exposes its browser runtime surface under the WP Suite global:
globalThis.WpSuite.plugins.flowglobalThis.WpSuite.plugins.flow.features
Readiness helpers
Like other WP Suite plugins, Flow supports runtime readiness helpers such as:
onReady(cb)availability()
You can also listen for readiness/error events emitted on the page:
wpsuite:flow:readywpsuite:flow:error
Example:
window.addEventListener("wpsuite:flow:ready", () => {
const flow = globalThis.WpSuite?.plugins?.flow;
console.log("Flow is ready", flow?.status);
});
window.addEventListener("wpsuite:flow:error", (event) => {
console.error("Flow failed to initialize", event.detail);
});
Store access
Flow exposes a lazily created store via:
WpSuite.plugins.flow.features.store
This is useful for advanced integrations that need access to Flow runtime state or configuration.
Modal API
Flow also exposes a light-DOM modal controller under WpSuite.plugins.flow.modals.
Available methods:
register(modalElement, options?)unregister(modalOrId)open(modalId, options?)close(modalId, returnValue?)toggle(modalId, options?)closeAll(returnValue?)isOpen(modalId)get(modalId)registerAction(actionName, handler)unregisterAction(actionName)
Supported lifecycle events:
wps-flow-modal:before-openwps-flow-modal:openwps-flow-modal:before-closewps-flow-modal:closewps-flow-modal:okwps-flow-modal:cancelwps-flow-modal:dismisswps-flow-modal:error
Action handlers registered through registerAction(actionName, handler) can be async. The modal runtime waits for the returned promise, keeps the modal open during the await, sets the modal into a busy state, disables the standard modal action triggers, and marks the currently running trigger element with data-wps-flow-pending="true" and aria-busy="true". That pending marker is intentionally generic, so it works with core Button blocks as well as custom button markup.
If an async handler returns false, automatic close is skipped and the modal stays open. For dismiss-style close paths such as the built-in close button, backdrop clicks, Escape, or programmatic close calls, the runtime also emits wps-flow-modal:dismiss.
Example: open a Flow modal from a neighboring Gutenberg button, close it from another Gutenberg button inside the modal, and listen for the close event:
- In the Flow Modal block inspector, set
modalIdtonewsletter-preferences. - Add a core Button block next to the modal and set its
Advanced -> Additional CSS class(es)towps-flow-modal-open--newsletter-preferences. - Add another core Button block inside the modal content and set its
Advanced -> Additional CSS class(es)towps-flow-modal-close. - Listen for
wps-flow-modal:closeondocument.
WordPress applies those custom classes on the rendered .wp-block-button wrapper, so the runtime resolves the trigger from the clicked inner link or button back to that wrapper.
<!-- Neighboring core/button block -->
<div class="wp-block-button wps-flow-modal-open--newsletter-preferences">
<a class="wp-block-button__link wp-element-button">
Open preferences
</a>
</div>
<!-- Inside the Flow Modal block -->
<div class="wp-block-button wps-flow-modal-close">
<a class="wp-block-button__link wp-element-button">
Close popup
</a>
</div>
<script>
document.addEventListener("wps-flow-modal:close", (event) => {
const detail = event.detail ?? {};
if (detail.modalId !== "newsletter-preferences") {
return;
}
console.log("Flow modal closed", {
modalId: detail.modalId,
returnValue: detail.returnValue,
});
// Example: reset host-page UI or fire analytics here.
});
</script>
For a core Button block, the CSS class route is usually the simplest integration because the runtime already delegates clicks for .wps-flow-modal-open--{modalId} and .wps-flow-modal-close. A .wps-flow-modal-close button closes the nearest Flow modal and emits wps-flow-modal:close with detail.returnValue === "close".
Modal-aware gallery openers
wps-flow-modal:open includes the triggerElement that opened the dialog, and WpSuite.plugins.flow.modals.open(modalId, { triggerElement }) accepts the same input for programmatic opens. The Flow Gallery block uses that opener element to resolve these optional hints:
wps-flow-gallery-target--{galleryId}ordata-wps-flow-gallery-target="{galleryId}"wps-flow-gallery-index--Nordata-wps-flow-gallery-index="N"
This lets a neighboring core Image or core Button block open a modal and land on a specific gallery image. If the modal contains only one Flow Gallery, the target hint can be omitted and the index alone is enough.
<div class="wp-block-image wps-flow-modal-open--product-lightbox wps-flow-gallery-target--product-gallery wps-flow-gallery-index--2">
<figure class="wp-block-image size-large">
<img src="/images/product-side.jpg" alt="Product side view" />
</figure>
</div>
const triggerElement = document.querySelector(".hero-product-image");
globalThis.WpSuite.plugins.flow.modals.open("product-lightbox", {
triggerElement,
});
Form-scoped field defaults
Flow core also exposes helper methods on WpSuite.plugins.flow for form-scoped field default values:
setFormFieldDefaultValue(formId, fieldName, value)setFormFieldDefaultValues(formId, values)clearFormFieldDefaultValues(formId)getFormFieldDefaultValue(formId, fieldName)getFormFieldDefaultValues(formId)
Each helper returns a Promise because Flow waits for its internal store before reading or writing values.
Example:
await globalThis.WpSuite.plugins.flow.setFormFieldDefaultValues("newsletter-footer", {
email: "user@example.com",
utm_source: "spring-campaign",
});
If you prefer working with the store directly, use the same actions through features.store and wp.data.dispatch(...):
const store = await globalThis.WpSuite.plugins.flow.features.store;
const actions = globalThis.wp.data.dispatch(store);
actions.setFormFieldDefaultValue("newsletter-footer", "email", "user@example.com");
These APIs write form-scoped defaults into the shared Flow store. Rendered Flow forms now resolve and apply those defaults by formId.
Runtime string interpolation
Some Flow runtime string settings support token interpolation. In practice that means Flow replaces {{...}} placeholders with values from the current page context before using the string.
This is useful for settings such as a per-form endpointPath or an API-backed options field's apiEndpoint when the final URL depends on the current page, query string, host-page globals, or current form values.
API-backed option fields can also derive initial selection state from each response item. Use apiSelectedPath to read a per-item flag or status field, and optionally apiSelectedValue when the item should count as selected only for a specific value such as SUBSCRIBED. This is especially useful for checkbox-group, and it also applies to select, radio, and tags fields.
For custom form submissions, the endpoint URL can be paired with an endpointMethod of GET, POST, PUT or PATCH. If no method is configured, Flow uses POST. When GET is selected, Flow sends the serialized field values as URL query parameters instead of a JSON request body. You can also attach additional browser-side request headers through endpointHeaders (stored as a JSON object string in block attributes); Flow merges them with the existing reCAPTCHA request header when present. Header values support the same runtime interpolation tokens as endpointPath, but they are resolved as plain strings instead of URL-encoded path segments.
When runtime tokens are used in endpointPath or an API-backed field's apiEndpoint, Flow URL-encodes dynamic field and query-string token values before inserting them into the final URL.
Supported token families are:
{{email}},{{fullName}},{{message}}, etc. for current form field values by field name{{query.foo}}— query-string values fromwindow.location.search{{location.href}},{{location.origin}},{{location.pathname}},{{location.search}},{{location.hash}}, etc.{{wp.postId}},{{wp.postSlug}},{{wp.postType}},{{wp.postTitle}},{{wp.postUrl}}{{wpsuite.apiBaseUrl}},{{wpsuite.siteSettings.siteId}}, etc. as a shorthand for the globalWpSuite.*object{{global.MyApp.config.formsBaseUrl}}for any other primitive value reachable onglobalThis{{field.email}},{{field.fullName}}, etc. as an explicit alias for current form field values
Examples:
{{location.origin}}/contact
https://api.example.com/forms?source={{location.pathname}}
{{wpsuite.apiBaseUrl}}/contact
{{global.MyApp.forms.submitUrl}}
{{wpsuite.contactApiBaseUrl}}/{{email}}
Important constraints:
- Interpolation is not arbitrary JavaScript execution. It only reads values from known runtime contexts or from property paths on
globalThis. - Function calls, expressions and conditionals such as
foo ? bar : bazare not supported. - For
global.*andwpsuite.*, the value must already exist on the page as a primitive (string,number, orboolean) when Flow resolves the setting. - Current form field value tokens are only available in runtime surfaces that already have access to the form state, such as rendered form fields and API-backed option loaders.
Capability decision
In packaged integrations, Flow also exposes helper functions such as waitForFlowReady(), getStore(), decideCapability() and resolveBackend() through the Flow core module. Those helpers are useful when you need to wait for initialization or decide whether the current site can use backend-aware features.
Runtime form events
Rendered Flow forms emit bubbling CustomEvents for important runtime actions. You can listen on the form host element or at document level because the events bubble and cross the shadow boundary.
document.addEventListener("smartcloud-flow:submit-success", (event) => {
console.log("Flow submit success", event.detail);
});
document.addEventListener("smartcloud-flow:wizard-step-change", (event) => {
console.log("Wizard moved", event.detail);
});
Current events include:
smartcloud-flow:draft-savedsmartcloud-flow:draft-loadedsmartcloud-flow:draft-deletedsmartcloud-flow:ai-suggestion-acceptedsmartcloud-flow:ai-suggestions-rejectedsmartcloud-flow:submit-after-ai-acceptedsmartcloud-flow:submit-successsmartcloud-flow:success-state-shownsmartcloud-flow:wizard-step-changesmartcloud-flow:return-to-formsmartcloud-flow:options-request-errorsmartcloud-flow:error
Each specific event also emits a generic smartcloud-flow:state-change event whose detail.event field contains the original event name.
Common detail fields include:
formIdactionsubmissionIdstatussuggestionId- wizard metadata such as
wizardPath,stepIndex,stepTitleandtotalVisibleSteps
For API-backed field loading errors, detail can also include fieldName, fieldType, requestMethod, errorType, message, status, and responseText.
Example: handling an API-backed options error from host-page JavaScript:
<div id="preferences-api-error" hidden></div>
<script>
const errorBox = document.getElementById("preferences-api-error");
document.addEventListener("smartcloud-flow:options-request-error", (event) => {
const detail = event.detail ?? {};
if (detail.formId && detail.formId !== "preferences-form") {
return;
}
const message =
typeof detail.message === "string" && detail.message.trim()
? detail.message.trim()
: "Unable to load options right now.";
if (errorBox) {
errorBox.textContent = message;
errorBox.hidden = false;
}
console.warn("Flow options request error", {
formId: detail.formId,
fieldName: detail.fieldName,
status: detail.status,
errorType: detail.errorType,
responseText: detail.responseText,
});
});
document.addEventListener("smartcloud-flow:submit-success", () => {
if (errorBox) {
errorBox.hidden = true;
errorBox.textContent = "";
}
});
</script>
Use detail.message for user-facing UI. Treat detail.responseText as diagnostic/debug data rather than something to show directly to end users.
This event layer is the recommended integration point when an outer application needs analytics, conversion tracking, custom redirects or host-page UI reactions.