Row Actions
Row actions let users do things to a single row (open a detail page, edit, delete, deactivate, send an email) directly from the table. Inertia Table renders them as links, buttons, dropdowns, or grouped menus, with optional confirmation dialogs and Laravel-style authorization gates, all defined in the same PHP Table class.
Introduction
Just like Row Links, Row Actions require an ActionColumn in the Table's column array.
use InertiaUI\Table\Columns;
class Users extends Table
{
public function columns(): array
{
return [
Columns\TextColumn::make('email'),
Columns\ActionColumn::new(),
];
}
}Grouping in Dropdown
Multiple Row Actions may be grouped in a dropdown to avoid cluttering the table with buttons. Use the asDropdown argument or method on the ActionColumn instance:
ActionColumn::new(asDropdown: true);
ActionColumn::new()->asDropdown();To set this as the default for all tables, call the static defaultAsDropdown() method on the ActionColumn class, typically in your AppServiceProvider:
use InertiaUI\Table\Columns\ActionColumn;
ActionColumn::defaultAsDropdown();Defining Row Actions
To define a Row Action, pass the label as the first argument and supply the handle argument. The handle argument is a callback that receives the model instance as its first argument.
use App\Models\User;
use InertiaUI\Table\Action;
class Users extends Table
{
public function actions(): array
{
return [
Action::make('Activate', handle: fn (User $user) => $user->activate()),
];
}
}The handle callback runs inside a regular HTTP request, so make sure the action completes quickly. For long-running work, dispatch a queued job instead.
Dynamic Labels
Instead of a static string, you may pass a closure to generate the label dynamically. The closure receives the model instance for row actions, and null for the bulk actions dropdown label.
use App\Models\User;
use InertiaUI\Table\Action;
Action::make(fn (?User $user) => $user ? "Notifications ({$user->unread_count})" : 'Notifications')
->handle(fn (User $user) => $user->markNotificationsAsRead());As a row action, the label includes the unread count for that specific user. In the bulk actions dropdown, where no single model is available, the closure receives null and should return a generic label.
Disabling Row Actions
You may disable a Row Action via the disabled() method or the disabled argument. Both accept a boolean or a callback that returns a boolean.
Action::make(
label: 'Activate',
handle: fn (User $user) => $user->activate()
disabled: !auth()->user()->is_admin
);A callback passed to the disabled argument receives the model as its only argument:
Action::make('Activate', handle: fn (User $user) => $user->activate())
->disabled(fn (User $user) => $user->is_active);The disabled state only affects the Row Action visually; it does not change the Bulk Actions dropdown. Users may still select a row and trigger a Bulk Action, but the action is skipped for disabled rows while iterating over the selection.
Hiding Row Actions
You may hide a Row Action via the hidden() method or the hidden argument. Just like disabled, it accepts a boolean or a callback that returns a boolean.
Action::make(
label: 'Restore',
handle: fn (User $user) => $user->restore(),
hidden: fn (User $user) => ! $user->trashed()
);Like disabling, the hidden state only affects the Row Action; it does not change the Bulk Actions dropdown. You may also make rows unselectable. See the Bulk Actions documentation for details.
Disabling and Hiding Simultaneously
Most of the time you want both behaviors under the same condition. Use the disabledAndHidden method or the disabledAndHidden argument to do both at once:
Action::make(
label: 'Delete',
handle: fn (User $user) => $user->delete(),
disabledAndHidden: fn (User $user) => $user->has_pending_invoice
);Redirecting After Action
You may redirect the user to a different URL after the action runs via the after argument on make(). It accepts a string URL or a callback that returns one.
Action::make(
label: 'Activate',
handle: fn (User $user) => $user->activate(),
after: '/activated-users'
);Confirmation Dialog
By default, Row Actions do not show a confirmation dialog before performing the action. To enable one, call the confirm() method on the Action instance:
Action::make('Delete', handle: fn (User $user) => $user->delete())->confirm();Here are the default texts for the confirmation dialog:
| Text | Default Value |
|---|---|
| Title | "Confirm action" |
| Message | "Are you sure you want to perform this action?" |
| Confirm Button | "Yes" |
| Cancel Button | "Cancel" |
You may customize the confirmation dialog by passing alternative texts to the confirm() method:
Action::make('Delete', handle: fn (User $user) => $user->delete())->confirm(
title: 'Delete User',
message: 'Are you sure you want to delete this user?',
confirmButton: 'Delete',
cancelButton: 'Cancel'
);Alternatively, you may pass the texts as named arguments to the make() method, but this also requires passing the confirmRequired argument as true:
Action::make(
label: 'Delete',
handle: fn (User $user) => $user->delete(),
confirmationRequired: true,
confirmationTitle: 'Delete User',
confirmationMessage: 'Are you sure you want to delete this user?',
confirmationConfirmButton: 'Delete',
confirmationCancelButton: 'Cancel'
);Row Action Styling
The Variant enum from Badge Column styling may also be used to style an Action Button, via the variant method or the variant argument:
use InertiaUI\Table\Variant;
Action::make(
label: 'Suspend',
handle: fn (User $user) => $user->suspend(),
variant: Variant::Danger
);You may also use one of the helper methods to style the Row Action:
Action::make(...)->asDangerButton();
Action::make(...)->asDefaultButton();
Action::make(...)->asInfoButton();
Action::make(...)->asPrimaryButton(); // alias of asInfoButton()
Action::make(...)->asSuccessButton();
Action::make(...)->asWarningButton();To skip the built-in variants, pass a custom class name to the buttonClass argument or the buttonClass() method:
Action::make('Special Action', buttonClass: 'btn-special');Alternatively, you may use the asButton() method and pass either a Variant enum or a custom class name:
Action::make('Mail Invoice')->asButton(Variant::Success);
Action::make('Special Action')->asButton('btn-special');Tooltips v4
You may attach a tooltip to any action. Disabled actions may show a separate tooltip explaining why they are unavailable:
Action::make('Archive')
->icon('heroicon-o-archive-box')
->hideLabel()
->tooltip('Archive this user')
->disabledTooltip('You need admin access to archive users');Tooltips are rendered as native title attributes on the action button. For enabled actions, the tooltip value is shown. For disabled actions, the disabledTooltip value takes precedence, falling back to tooltip if not set.
Dynamic Confirmation Messages v4
The confirmation title and message support :attribute placeholders that are resolved against the model's data at the time the action is triggered. This lets you include record-specific details in the dialog text.
// Row action: model attributes as placeholders
Action::make('Delete', handle: fn (User $user) => $user->delete())
->confirm(
title: 'Delete :name',
message: 'This will permanently delete :name (:email).',
);For bulk actions, use :count to show the number of selected items. The title and message parameters accept a string or an array with singular and plural forms:
Action::make('Delete')
->onlyAsBulkAction()
->confirm(
message: ['Delete :count user?', 'Delete :count users?'],
);A third array element handles the select-all case, where the sentence structure may differ (especially in non-English languages):
Action::make('Delete')
->onlyAsBulkAction()
->confirm(
title: [
'Confirm deletion of :count user',
'Confirm deletion of :count users',
'Confirm deletion of all users',
],
message: [
'Delete :count user?',
'Delete :count users?',
'Delete all users?',
],
);Without the third element, the plural form is used for select-all. A plain string still works when pluralization is not needed. Placeholders use word boundaries, so :count will not match inside longer names like :country.
HTTP Methods v4
By default, row actions that use url() navigate via a standard Inertia GET visit. You may override the HTTP method for that visit:
Action::make('Approve')
->url(fn (User $user) => route('users.approve', $user))
->method('POST');
Action::make('Delete')
->url(fn (User $user) => route('users.destroy', $user))
->method('DELETE')
->asDangerButton();On the frontend, non-GET actions use router.visit() with the specified method, preserving scroll position. This works with any method supported by Inertia: POST, PUT, PATCH, or DELETE.
Prefetch v4
You may instruct Inertia to prefetch the action URL before the user clicks it by calling prefetch() on the Url object. See the Row Links prefetch documentation for the full list of strategies and the cacheFor option.
Action::make('Edit')
->url(fn (User $user, Url $url) => $url->route('users.edit', $user)->prefetch());Custom Actions in the Frontend
To handle the action in the frontend instead of PHP, omit the handle argument:
Action::make('Custom Action');In the frontend, define a callback to handle the action. It receives the action object, an array of keys, and an onFinish callback that must be invoked to clear the loading state.
<script setup>
import { Table } from '@inertiaui/table-vue'
function handleCustomAction(action, keys, onFinish) {
// Perform the action...
onFinish()
}
</script>
<template>
<Table :resource="users" @custom-action="handleCustomAction" />
</template>import { Table } from '@inertiaui/table-react'
function handleCustomAction(action, keys, onFinish) {
// Perform the action...
onFinish()
}
export default function Users({ users }) {
return (
<Table resource={users} onCustomAction={handleCustomAction} />
)
}To make the action more recognizable in the frontend, use the id argument or method to give it a unique identifier:
Action::make('Activate', id: 'activate_user');
Action::make('Delete')->id('delete_user');Alternatively, you may use Metadata to attach additional data to the action:
Action::make('Activate')->meta(['using' => 'customActivator']);The frontend may access this data via the meta property on the action object:
function handleCustomAction(action, keys, onFinish) {
if (action.id === 'activate_user') {
// Perform the action with the ID 'activate_user'
}
if (action.meta.using === 'customActivator') {
// Perform the action using a custom activator
}
onFinish()
}Success and Error Events
You may define a callback to run after an action succeeds or fails. These events are not fired for custom actions.
Success Event
The callback receives the action object and an array of keys as arguments.
<script setup>
import { Table } from '@inertiaui/table-vue'
function handleActionSuccess(action, keys) {
console.log(`Action '${action.id}' with keys ${keys.join(',')} was successful`)
}
</script>
<template>
<Table :resource="users" @action-success="handleActionSuccess" />
</template>import { Table } from '@inertiaui/table-react'
function handleActionSuccess(action, keys) {
console.log(`Action '${action.id}' with keys ${keys.join(',')} was successful`)
}
export default function Users({ users }) {
return (
<Table resource={users} onActionSuccess={handleActionSuccess} />
)
}Error Event
By default, the component shows a dialog with a generic error message when an action fails. Passing a callback suppresses the dialog, and you take full responsibility for handling the error.
<script setup>
import { Table } from '@inertiaui/table-vue'
function handleActionError(action, keys, error) {
console.error(`Action '${action.id}' with keys ${keys.join(',')} failed with error: ${error}`)
}
</script>
<template>
<Table :resource="users" @action-error="handleActionError" />
</template>import { Table } from '@inertiaui/table-react'
function handleActionError(action, keys, error) {
console.error(`Action '${action.id}' with keys ${keys.join(',')} failed with error: ${error}`)
}
export default function Users({ users }) {
return (
<Table resource={users} onActionError={handleActionError} />
)
}Data Attributes and Metadata
See the Row Links documentation for details on adding Data Attributes and Metadata to Row Actions.