Why Custom Actions Exist
Standard apps cover create, edit, delete, and save. Real business processes need more: approve, release, reject, copy, submit for review. Custom actions put those verbs into the UI as buttons, exactly where users expect them.
In Fiori Elements, a custom action is a backend operation surfaced in the UI. You declare it once in the service, and the template renders the button, handles the busy state, and refreshes the data afterwards.
Declaring the Action in the Service
Actions are declared on the entity in CDS. A bound action operates on one or more selected records; an unbound action is global to the service. Most custom buttons are bound actions on the entity.
action releaseOrder() returns SalesOrder;
That single line in the service definition is the contract. The backend implements the logic; the UI only needs to know the action exists, what it is called, and what it returns.
Surfacing It with UI.DataFieldForAction
The button appears through the UI.DataFieldForAction annotation. Put it in UI.LineItem and the button lands in the list report toolbar. Put it in UI.Identification and it appears on the object page header.
@UI.lineItem: [ {
type: #FOR_ACTION,
dataAction: 'releaseOrder',
label: 'Release'
} ]
Label is what the user reads, so write it as a verb. "Release" beats "Release Order" — the context is already obvious from the screen the user is on.
Key takeaway: the action lives in the backend; the annotation only decides where the button renders. Keep business logic out of the UI layer.
Bound vs. Unbound, and Multi-Select
A bound action on the entity works against the current record on the object page, or against every selected row in the list report. Fiori Elements handles multi-select for you — select five orders, press Release, and the action fires for each.
Unbound actions belong in the list report toolbar for operations that are not tied to a record, like "Create from template". Declare them at the service level and reference them the same way.
Enabling and Disabling Dynamically
A Release button that is always clickable is a bug waiting to happen. Actions support dynamic enablement through annotations: the button enables only when the record's state allows it.
The usual mechanism is a boolean property or operation-available annotation evaluated per row. Say an order can only be released when its status is "Open" — the backend exposes that condition, and the UI disables the button for released orders automatically. Users never see a button they cannot use, which is far better than an error dialog after the click.
Every custom action needs three things: the backend operation, the annotation placing the button, and the enablement rule. Skip the third and you will get support tickets.
Confirmation and Result Handling
Destructive or significant actions should ask first. Criticality annotations mark an action as needing confirmation, and the template shows the dialog with your message. After the action runs, the returned entity refreshes the UI — the user sees the new status immediately without a manual reload.
Keep actions small and single-purpose. One action, one verb, one outcome. If the button label needs the word "and", split it into two actions.
What's the most useful custom action you've added to a Fiori Elements app? Tell me in the comments.