Actions

Actions connect UI elements to Ferro handlers for navigation, form submission, and destructive operations. Actions are declared as an "action" field on any element in the "elements" map.

How Actions Work

Every interactive element can carry an "action" field alongside its "type" and "props". Actions reference handler names (e.g., "users.store") instead of raw URLs. The framework resolves handler names to URLs at render time using the route registry.

  • GET actions render as links (navigation)
  • Non-GET actions (POST, PUT, PATCH, DELETE) render as form submissions
  • Actions can require confirmation before executing
  • "on_success" and "on_error" control what happens after the server responds

Basic Action Example

"delete_btn": {
  "type": "Button",
  "props": {
    "label": "Delete",
    "variant": "destructive"
  },
  "action": {
    "handler": "items.destroy",
    "method": "DELETE"
  }
}

The "action" object lives directly on the element. "handler" is the route name; "method" is the HTTP verb.

HTTP Methods

Available methods: "GET", "POST", "PUT", "PATCH", "DELETE".

"method" defaults to "POST" when omitted.

"save_btn": {
  "type": "Button",
  "props": { "label": "Save" },
  "action": {
    "handler": "items.update",
    "method": "PUT"
  }
}

Confirmation Dialogs

Add a "confirm" object to show a confirmation dialog before the action executes:

"delete_btn": {
  "type": "Button",
  "props": {
    "label": "Delete",
    "variant": "destructive"
  },
  "action": {
    "handler": "items.destroy",
    "method": "DELETE",
    "confirm": {
      "title": "Delete this item?",
      "variant": "danger"
    }
  }
}

The "confirm" object fields:

FieldTypeDescription
"title"stringDialog heading text
"message"string (optional)Additional detail text
"variant"string"default" or "danger"

Standard confirmation (no destructive styling):

"action": {
  "handler": "items.store",
  "method": "POST",
  "confirm": {
    "title": "Save changes?"
  }
}

Action Outcomes

The "on_success" and "on_error" fields control behavior after the server responds. Each is an object with a "type" discriminator.

Redirect

Navigate to a URL after success:

"action": {
  "handler": "items.store",
  "method": "POST",
  "on_success": {
    "type": "redirect",
    "url": "/items"
  }
}

Reload

Reload the current page:

"action": {
  "handler": "settings.update",
  "method": "PUT",
  "on_success": {
    "type": "reload"
  }
}

Notify

Show a notification toast:

"action": {
  "handler": "items.store",
  "method": "POST",
  "on_success": {
    "type": "notify",
    "message": "Item created",
    "variant": "success"
  }
}

Notification variants: "success", "info", "warning", "error".

Show errors

Display validation errors returned from the handler on corresponding form fields:

"action": {
  "handler": "items.store",
  "method": "POST",
  "on_error": {
    "type": "show_errors"
  }
}

Combined example

"action": {
  "handler": "items.store",
  "method": "POST",
  "on_success": {
    "type": "redirect",
    "url": "/items"
  },
  "on_error": {
    "type": "show_errors"
  }
}

Form Actions

The Form element uses "action" as its submit action. The entire form submits to the handler when the user clicks the submit button.

Complete form element example:

"create_form": {
  "type": "Form",
  "props": {},
  "action": {
    "handler": "items.store",
    "method": "POST",
    "on_success": {
      "type": "redirect",
      "url": "/items"
    },
    "on_error": {
      "type": "show_errors"
    }
  },
  "children": ["name_input", "description_input", "submit_btn"]
}

With form fields as sibling elements:

"elements": {
  "create_form": {
    "type": "Form",
    "props": {},
    "action": {
      "handler": "items.store",
      "method": "POST",
      "on_success": { "type": "redirect", "url": "/items" },
      "on_error": { "type": "show_errors" }
    },
    "children": ["name_input", "submit_btn"]
  },
  "name_input": {
    "type": "Input",
    "props": {
      "field": "name",
      "label": "Name",
      "input_type": "text",
      "required": true
    }
  },
  "submit_btn": {
    "type": "Button",
    "props": { "label": "Create" }
  }
}

GET actions render as links. Use "method": "GET" on any element to make it a navigation link:

"view_btn": {
  "type": "Button",
  "props": { "label": "View Details" },
  "action": {
    "handler": "items.show",
    "method": "GET"
  }
}