Skip to main content

Custom Action

Overview​

Custom actions let you add your own buttons, links, modals, and pages to a model beyond the default index, show, new, edit, and destroy actions. They are defined with the custom_action DSL method inside a model's cm-admin configuration and can run arbitrary Ruby code against a record (or the whole collection).

Typical use cases:

  • State transitions — approve, archive, publish, or soft-delete a record.
  • Quick edits — update a subset of fields through a modal or dedicated page.
  • Exports / bulk fetches — trigger a collection-level operation.
  • External links — send the user to another URL related to the record.

Syntax​

custom_action name: 'action_name', route_type: 'member', verb: 'patch',
path: ':id/action_name', display_type: :button do
record = ::YourModel.find(params[:id])
# perform the operation
record
end

The block receives access to params and must return the affected record (or collection). The returned object is used to render the response and to evaluate success_message / error_message.

Options​

OptionTypeDescription
nameStringUnique identifier for the action. Used to build the route and controller method.
display_nameStringHuman-friendly label shown on the button/link. Falls back to name.titleize.
verbStringHTTP verb: get, post, put, patch, or delete.
pathStringURL path. Use :id for member routes (e.g. ':id/approve').
route_typeStringmember (operates on a single record) or collection (operates on many).
display_typeSymbolHow the action is rendered. See Display types.
display_ifProcLambda receiving the record; return true to show the action.
icon_nameStringFont Awesome icon class, e.g. 'fa-regular fa-circle-check'.
icon_styleStringInline icon style, e.g. '--fa-primary-color: #ff6b6b;'.
modal_configurationHashModal title, description, and confirmation_text for modal display types.
page_titleStringTitle shown for :page / :form_page display types.
page_descriptionStringDescription shown for :page / :form_page display types.
group_nameStringGroups actions together in the dropdown (see Group Action).
show_on_indexBooleanWhether the action appears in the index row dropdown. Defaults to true.
redirect_toProcLambda returning a URL to redirect to (used by :link).
success_messageProcLambda receiving the record; returns an alert Hash { header:, body:, icon: }.
error_messageProcLambda receiving the record and its errors; returns an alert Hash { header:, body:, icon: }.
alert_typeSymbolSuccess alert style: :flash (default) or :banner.

Display types​

The display_type option controls how the action is presented:

  • :button — A button that submits directly and runs the block. Best for one-click state changes.
  • :modal — Shows a confirmation modal before submitting. Configure copy via modal_configuration.
  • :form_modal — Opens a modal containing a form. Requires a form block and an on_submit block.
  • :page — Renders a dedicated page. Use page_title / page_description.
  • :form_page — A full-page form. Requires a form block and an on_submit block.
  • :link — Renders a link to an external/related URL via redirect_to.
  • :icon_only — Renders only the icon with no label.

Examples​

Button action​

Runs immediately when clicked. Here display_if hides the action unless the record is a draft.

custom_action name: 'activate', display_name: 'Activate', route_type: 'member', verb: 'post',
icon_name: 'fa-regular fa-power-off', path: ':id/activate',
display_type: :button, display_if: ->(df) { !df.is_active? } do
demo_form = ::DemoForm.find(params[:id])
demo_form.update!(is_active: true)
demo_form
end

Prompts the user to confirm before running the block.

custom_action name: 'approve', display_name: 'Approve', route_type: 'member', verb: 'patch',
icon_name: 'fa-regular fa-circle-check', path: ':id/approve',
display_type: :modal, display_if: lambda(&:draft?),
modal_configuration: { title: 'Approve Demo Form',
description: 'Are you sure you want to approve this demo form?',
confirmation_text: 'Approve' } do
demo_form = ::DemoForm.find(params[:id])
demo_form.approved!
demo_form
end

Form modal action​

Collects input through a form rendered in a modal. Requires both a form block (defining the fields) and an on_submit block (handling the update).

custom_action name: 'assign_user', display_name: 'Assign User', verb: 'post', route_type: 'member',
icon_name: 'fa-regular fa-user', display_type: :form_modal,
path: ':id/assign_user',
modal_configuration: { title: 'Select User', confirmation_text: 'Assign' } do
form do
cm_section '' do
alert_box body: 'This will assign a new user to the demo form.', type: :info
form_field :user_id, input_type: :radio_button_group, helper_method: :select_options_for_user, label: 'User'
end
end
on_submit do
@demo_form = ::DemoForm.find(params[:id])
@demo_form.update!(user_id: params.dig(:demo_form, :user_id))
@demo_form
end
end

Note: For :form_modal and :form_page, the form block is mandatory. Submitted params are nested under the model name, e.g. params[:demo_form][:user_id].

Collection action​

Use route_type: 'collection' for actions that operate on the whole collection rather than a single record. These paths do not include :id.

custom_action name: 'export_all', display_name: 'Export All', route_type: 'collection', verb: 'get',
icon_name: 'fa-regular fa-download', path: 'export_all',
display_type: :button do
::DemoForm.all
end

Styled action​

Customize the icon appearance with icon_style.

custom_action name: 'styled_action', display_name: 'Styled Action', route_type: 'member', verb: 'post',
icon_name: 'fa-regular fa-palette',
icon_style: '--fa-primary-color: #ff6b6b; --fa-secondary-color: #4ecdc4;',
path: ':id/styled_action', display_type: :button do
demo_form = ::DemoForm.find(params[:id])
demo_form
end

Custom success and error messages​

success_message and error_message are lambdas that return an alert Hash with :header, :body, and :icon keys. success_message receives the record; error_message receives the record and its errors. Any key you omit falls back to a sensible default.

custom_action name: 'approve', display_name: 'Approve', route_type: 'member', verb: 'patch',
icon_name: 'fa-regular fa-circle-check', path: ':id/approve',
display_type: :button,
success_message: lambda { |demo_form|
{ header: 'Approved', body: "#{demo_form.name} was approved.", icon: 'fa fa-check-circle' }
},
error_message: lambda { |_demo_form, errors|
{ header: 'Approval failed', body: errors.full_messages.to_sentence, icon: 'fa fa-circle-xmark' }
} do
demo_form = ::DemoForm.find(params[:id])
demo_form.approved!
demo_form
end
  • Group Action — group multiple custom actions under a common heading.
  • Bulk Action — run an action across multiple selected records.
  • Adding Alert — configure success and error alerts.