Hosting an Aura Action on a Flow Screen (Configuration Guide for GenericAuraActionToFlowAdapter)

The GenericAuraActionToFlowAdapter flow screen component places any access="global" Aura component onto a flow screen and advances the flow when the component finishes.

Adding an action is configuration only. No code change and no release is required.

Who Should Use This? Anyone hosting an existing action on a flow-driven surface (new USAM, Omnitable), and anyone maintaining one already hosted.

Building something new? Do not use this adapter. The adapter exists so that an existing Aura action can go on a flow screen without being rewritten. A new action should be flow-native from the start. Use the adapter only when rewriting an existing action is not viable.

Before You Begin

The component you want to host must meet two requirements. There is no interface to implement, no event to fire, and no callback to declare.

Requirement 1: Be Reachable (access="global")

The adapter creates the component with $A.createComponent from inside the TR1 package. That crosses a namespace boundary, so the component and every attribute you intend to set must be marked global:

Copy
<aura:component access="global">
    <aura:attribute name="recordIds" type="List" access="global" default="[]" />
    ...
</aura:component>

An attribute left at default access is silently unset. The component is created but arrives with its default value, which looks identical to a misspelled attribute name.

Requirement 2: Destroy Itself When Finished (component.destroy())

Destruction is the completion signal. When the hosted component calls destroy(), the adapter navigates the flow.

Copy
({
    finish: function (component) {
        component.destroy();
    }
});

Most existing action components already do this as self-destruction on completion is the established convention for modal actions in this codebase. Such a component works under the adapter unmodified.

Configure the Flow Screen

In Flow Builder, add a Screen element and drag GenericAuraActionToFlowAdapter (under Custom components) onto it.

Adapter Attributes

Attribute Label in Flow Builder Required Purpose
componentName Component Name Yes The component to create, for example TR1:MyCustomAction or c:MyCustomAction.
attributesJson Attributes (JSON) No Every input for that component, as a single JSON object.
flowNavigateAfter Navigate After No (Default: NEXT) What the flow does when the component destroys itself: NEXT or FINISH.
  • componentName: Passed verbatim to $A.createComponent, so the prefix must match where the component actually lives. A component in a managed package takes that package's namespace (TR1:MyCustomAction). Use c:MyCustomAction only for an unpackaged component in the org's default namespace: c: is resolved at runtime against the org default namespace, not against the adapter's TR1 namespace.

  • flowNavigateAfter: If the requested action is not offered by the flow at that moment (e.g., a final screen has no NEXT), the adapter falls back to NEXT if available, otherwise FINISH. Navigation happens once. A second completion signal is ignored.

Screen Settings

Set these on the Screen element itself:

Setting Recommended Value Why
Show Footer Off (showFooter=false) The hosted component owns its own buttons. A flow footer gives the user a second Next that bypasses the action.
Show Header Off (showHeader=false) The action supplies its own title and chrome.
Allow Back Off Re-entering a completed action is rarely meaningful.
Allow Pause Off The action's own state is not resumable.

The adapter renders no chrome of its own (no modal, no backdrop, no buttons). If the action needs a modal frame, the modal belongs to the action or to the surface launching the flow.

Build attributesJson

attributesJson must be a single JSON object with attribute names as keys and values as the JSON type the component declares. Omit it and the component is created with its own defaults.

Copy
{
    "recordIds": ["a0X1t000000abcAAA", "a0X1t000000abdAAA"],
    "jobId": "a1B1t000000xyzAAA",
    "maxRows": 25,
    "isReadOnly": true,
    "ratio": 0.75,
    "startDate": "2026-08-19",
    "createdAt": "2026-08-19T14:01:00.000Z",
    "config": { "mode": "bulk", "stages": ["Submitted", "Interview"] }
}

Compose the JSON in Flow

Flow formulas cannot iterate a collection, so any collection variable must be flattened before it can go into JSON. Use the following steps:

  1. Join the collection: Add a Subflow element calling BH_Join_Collection.
    • Set textCollection: to your collection variable, for example {!recordIds}.
    • Set separator:,.
    • Store the joinedString output in a Text variable, for example {!joinedRecordIds}.
  2. Build the JSON in a formula: Create a Text formula, for example attributesJSON:
    Copy
    IF(ISBLANK({!joinedRecordIds}),
      '{ "recordIds" : [] }',
      '{ "recordIds" : [' + '"' + SUBSTITUTE({!joinedRecordIds}, ',', '","') + '"' + '] }'
    )
  3. Reference the formula: Use this formula in the screen component's attributesJSON input.

Escaping Rules

  • Flow formula string literals are single-quoted, so " inside them need no escaping.
  • Substitute out double quotes in free text before concatenating to avoid breaking the JSON.
  • Do not wrap numbers or booleans in quotes unless the target attribute is genuinely a String.

Pass Data Types Correctly

The following data types carry faithfully through JSON into the corresponding Aura attribute type:

Aura Attribute Type JSON Format Example
String string "a0X1t000000abcAAA"
Integer number 25
Decimal, Double number 0.75
Boolean boolean true
List array ["a", "b"]
Object, Map object { "mode": "bulk" }
Date ISO date string "2026-08-19"
DateTime ISO datetime string "2026-08-19T14:01:00.000Z"

Caveats:

  • Long silently truncates above 2^53 (JSON numbers are IEEE 754 doubles).
  • type="Set" is not a real Set. Duplicates are not removed; supply an array and treat it as a list.
  • Aura.Component and facet attributes cannot be supplied at all.

Register the Action on an Omnitable

Each hosted action gets its own flow and its own entry in the Omnitable's action configuration. The adapter itself is never registered and there is no single generic entry covering every adapter-hosted action. For an action called MyCustomAction:

Copy
{
    "actionName": "MyCustomAction",
    "actionLabel": "My Custom Action",
    "flowName": "TR1__MyCustomAction_Flow",
    "modalSize": "medium",
    "icon": "utility:touch_action",
    "variablesMapping": {
        "selectedIds": "recordIds"
    }
}
  • actionName, actionLabel: The name and label of this action, not of the adapter.
  • flowName: The full API name of the flow hosting this action, namespace-prefixed.
  • modalSize: The modal the surface draws around the flow. Set to none if the custom component has its own modal markup, to prevent a duplicate backdrop or double modal.
  • variablesMapping: Maps the surface's context to your flow's input variables. The flow's target variable must be marked Available for input.

End-to-End Checklist for Hosting an Existing Action

  1. Component: Confirm access="global" is on the component and every attribute the flow will set.
  2. Completion: Confirm the component calls component.destroy() when finished.
  3. Flow: Create an input variable for any required context, and mark it Available for input.
  4. Payload: Join any collections using BH_Join_Collection, then build attributesJSON in a formula.
  5. Screen: Add GenericAuraActionToFlowAdapter, configure its inputs, and turn off Show Footer, Show Header, Allow Back, and Allow Pause.
  6. Register: Add the action JSON to the Omnitable.
  7. Verify: Check the values the component actually received. For example, output {!v.recordIds.length} to confirm an array of two IDs reads 2 and not the character count of a joined string.

Troubleshooting

Errors render inline on the screen in red, from translatable custom labels (GenericAuraAction_*).

Error Message Cause Fix
No component name was supplied. componentName is empty. Check formula inputs. A literal value confirms whether the rest of the configuration is correct.
The attributes could not be read as JSON: ... Not valid JSON. Paste the value into a JSON validator. Look for unescaped ", trailing commas, or single quotes.
...must be a JSON object... Valid JSON, but an array/string/null. Wrap the value: { "recordIds": [...] }
Create component returned: ... $A.createComponent failed. Usually the component is not access="global", or the prefix does not match the component's package.

Silent Symptoms (No Error Shown)

  • Attribute at default: The attribute name is misspelled, or the attribute is not access="global".
  • List attribute has one character per element: A String was supplied where a List was declared.
  • Number behaves like text: The value was quoted in the JSON.
  • Screen never advances: The component never calls destroy().

Limitations

  • Nothing is validated. Misspelled attributes or wrong-typed values are silently ignored or applied by the platform.
  • A component that never destroys itself hangs the flow. There is no timeout or escape hatch.
  • A configuration error also hangs the flow. The error message renders, but the screen does not advance.
  • Latency: There is up to 500ms of latency between the action finishing and the screen moving.