Skip to main content

CoreActionForm Component

The CoreActionForm component extends CoreForm to provide seamless integration with CoreAction backend operations. It automatically handles action execution, validation error display, loading states, and confirmation dialogs.
Related Documentation:

Overview

CoreActionForm wraps CoreForm and adds action-specific functionality:
  • Automatic Action Execution - Executes CoreAction on form submission
  • Parameter Merging - Combines form data with params/getParams following priority rules
  • Validation Error Handling - Automatically displays backend validation errors on form fields
  • Loading States - Manages loading state from useCoreAction hook
  • Confirmation Dialogs - Shows confirmation prompts for actions marked with confirm: true
  • Model Binding - Supports automatic Eloquent model resolution from IDs

Basic Usage

import {
  CoreActionForm,
  CoreFormInput,
  CoreFormSubmitButton,
} from '@/core/components/core-form';
import { useForm } from '@inertiajs/react';

export default function SendEmailForm({ sendEmailAction }) {
  const form = useForm({
    email: '',
    subject: '',
    message: '',
  });

  return (
    <CoreActionForm
      action={sendEmailAction}
      form={form}
      footer={
        <CoreFormSubmitButton variant="brand">
          {__('Send Email')}
        </CoreFormSubmitButton>
      }
    >
      <CoreFormInput name="email" label={__('Email')} type="email" required />
      <CoreFormInput
        name="subject"
        label={__('Subject')}
        type="text"
        required
      />
      <CoreFormTextarea name="message" label={__('Message')} required />
    </CoreActionForm>
  );
}

Uncontrolled Mode

import { CoreActionForm, CoreFormInput } from '@/core/components/core-form';

export default function SendEmailForm({ sendEmailAction }) {
  return (
    <CoreActionForm action={sendEmailAction}>
      {({ data, setData, errors, processing }) => (
        <>
          <CoreFormInput name="email" label={__('Email')} type="email" />
          <CoreFormInput name="subject" label={__('Subject')} type="text" />
          <CoreFormTextarea name="message" label={__('Message')} />
        </>
      )}
    </CoreActionForm>
  );
}

Props

CoreActionForm extends all CoreForm props and adds action-specific props:

Action-Specific Props

  • action: CoreActionData (required) - The action to execute on form submission
  • params?: Record<string, any> - Static parameters to merge with form data
  • getParams?: () => Record<string, any> - Dynamic parameter function computed at execution time
  • onSuccess?: () => void - Success callback fired when action completes successfully
  • onError?: (error: any) => void - Error callback fired for non-validation errors
  • onValidationError?: (errors: any) => void - Validation error callback (errors are also automatically displayed on fields)

Inherited CoreForm Props

All CoreForm props are available:
  • form?: Inertia form object (for controlled mode)
  • footer?: ReactNode to render in the form footer
  • disabled?: Boolean to disable all form fields
  • className?: Additional CSS classes for the form layout
  • children: ReactNode or function that receives form props

How Action Submission Works

  1. User submits form → Form data is collected from all form fields
  2. Parameters are merged → Form data is automatically merged with params/getParams (if provided)
  3. Confirmation check → If action has confirm: true, shows confirmation dialog
  4. Action executesuseCoreAction hook executes the action with merged parameters
  5. Validation → Backend validates parameters using action’s rules() method
  6. Error Display → Validation errors automatically appear on corresponding form fields
  7. Success/Error → Callbacks are fired based on result

Parameter Merging

Form data is always included. Parameters follow this priority order (highest to lowest):
  1. getParams() - Dynamic params computed at execution time (overrides form data)
  2. params prop - Static params passed to component (overrides form data)
  3. Form data - Values from form fields
  4. Backend defaults - Default params from action’s params() method

Dynamic Parameters with getParams

Use getParams to include data not in the form or transform form data:
export default function BanUserForm({ banUserAction, user }) {
  const form = useForm({
    reason: '',
    duration: '30',
  });

  return (
    <CoreActionForm
      action={banUserAction}
      form={form}
      getParams={() => ({
        user: user.id, // Add external data
        duration: parseInt(form.data.duration), // Transform form data
        banned_at: new Date().toISOString(), // Add computed value
        // reason is automatically included from form.data.reason
      })}
    >
      <CoreFormTextarea name="reason" label={__('Ban Reason')} required />
      <CoreFormSelect
        name="duration"
        label={__('Duration')}
        options={durations}
      />
    </CoreActionForm>
  );
}

Overriding Parameters

Use params prop to add parameters that aren’t in the form:
<CoreActionForm
  action={sendEmailAction}
  form={form}
  params={{
    template: 'welcome', // Not in form, but needed by action
    priority: 'high',
  }}
>
  {/* Form fields */}
</CoreActionForm>

Validation Error Handling

Validation errors from action rules are automatically displayed on form fields: Backend Action:
// Backend action
protected function rules(): array
{
    return [
        'email' => ['required', 'email'],
        'subject' => ['required', 'string', 'max:255'],
    ];
}
Frontend Form:
// Frontend - errors automatically appear on fields
<CoreFormInput name="email" label={__('Email')} />
// Error message appears below the email field if validation fails
The onValidationError callback is also fired, allowing you to handle errors programmatically:
<CoreActionForm
  action={createUserAction}
  form={form}
  onValidationError={errors => {
    // Validation errors are displayed automatically, but you can handle them here too
    console.log('Validation errors:', errors);
  }}
>
  {/* Form fields */}
</CoreActionForm>

Success/Error Callbacks

<CoreActionForm
  action={createUserAction}
  form={form}
  onSuccess={() => {
    router.visit(route('users.index'));
  }}
  onError={error => {
    console.error('Action failed:', error);
  }}
  onValidationError={errors => {
    // Validation errors are displayed automatically, but you can handle them here too
    console.log('Validation errors:', errors);
  }}
>
  {/* Form fields */}
</CoreActionForm>

Model Binding

Actions automatically resolve Eloquent models from IDs. Pass model IDs in form data or via getParams:
export default function AssignUserForm({ assignUserAction, project }) {
  const form = useForm({
    user_id: '',
  });

  return (
    <CoreActionForm
      action={assignUserAction}
      form={form}
      getParams={() => ({
        project: project.id, // Model ID - backend resolves to Project model
        user: form.data.user_id, // Model ID - backend resolves to User model
      })}
    >
      <CoreFormSearchCombobox
        name="user_id"
        label={__('Assign To')}
        searchComboboxProps={{
          url: route('users.search'),
          displayValue: user => user.name,
          getValue: user => user.id.toString(),
        }}
      />
    </CoreActionForm>
  );
}
Backend action automatically resolves models:
protected function handle(Project $project, User $user): mixed
{
    // $project and $user are automatically resolved from IDs
    $project->assignTo($user);
    return redirect()->back()->withSuccess(__('User assigned successfully'));
}

Confirmation Dialogs

Actions marked with confirm: true automatically show a confirmation dialog before execution: Backend Action:
protected function defaults(): void
{
    $this->label('Delete User');
    $this->confirm(true);
    $this->confirmMessage('Are you sure you want to delete this user? This action cannot be undone.');
}
Frontend Form:
<CoreActionForm action={deleteUserAction} form={form}>
  {/* Confirmation dialog appears automatically on submit */}
</CoreActionForm>
The confirmation dialog:
  • Shows the action’s label as the title
  • Shows confirmMessage (or default message) as description
  • Uses the action’s variant for the confirm button
  • Prevents form submission until confirmed

Action vs Regular Form Submission

FeatureRegular Form (onSubmit/action URL)Action Form (CoreActionForm)
SubmissionManual handler or URLAutomatic action execution
ValidationManual error handlingAutomatic from action rules
Loading StateManual processing stateAutomatic from useCoreAction
ParametersForm data onlyForm data + action params
BackendGeneric routesCoreAction system
ConfirmationManual implementationAutomatic for confirm: true

Complete Example

Backend Action:
<?php

namespace App\Http\Controllers\Actions;

use App\Models\User;
use Inly\Core\Actions\CoreAction;
use Inly\Core\Enums\CoreVariant;

class SendWelcomeEmailAction extends CoreAction
{
    protected function defaults(): void
    {
        $this->label('Send Welcome Email');
        $this->icon('Mail');
        $this->variant(CoreVariant::BRAND);
    }

    public function authorize(): bool
    {
        return auth()->user()->can('send-emails');
    }

    protected function rules(): array
    {
        return [
            'user' => ['required', 'exists:users,id'],
            'subject' => ['required', 'string', 'max:255'],
            'message' => ['required', 'string', 'max:5000'],
            'include_attachments' => ['boolean'],
        ];
    }

    protected function handle(User $user, string $subject, string $message, bool $includeAttachments = false): mixed
    {
        Mail::to($user->email)->send(new WelcomeEmail($user, $subject, $message, $includeAttachments));
        return redirect()->back()->withSuccess(__('Welcome email sent to :name!', ['name' => $user->name]));
    }
}
Frontend Form:
import {
  CoreActionForm,
  CoreFormInput,
  CoreFormTextarea,
  CoreFormSwitch,
  CoreFormSubmitButton,
} from '@/core/components/core-form';
import { useForm } from '@inertiajs/react';

export default function SendWelcomeEmailForm({ user, sendWelcomeEmailAction }) {
  const form = useForm({
    subject: `Welcome to our platform, ${user.name}!`,
    message: `Hi ${user.name},\n\nWelcome to our platform!`,
    include_attachments: false,
  });

  return (
    <CoreActionForm
      action={sendWelcomeEmailAction}
      form={form}
      getParams={() => ({
        user: user.id, // Pass user ID, backend resolves to User model
      })}
      footer={
        <CoreFormSubmitButton variant="brand" iconLeft="Mail">
          {__('Send Welcome Email')}
        </CoreFormSubmitButton>
      }
    >
      <CoreFormInput
        name="subject"
        label={__('Email Subject')}
        type="text"
        required
      />
      <CoreFormTextarea name="message" label={__('Email Message')} required />
      <CoreFormSwitch
        name="include_attachments"
        label={__('Include Attachments')}
      />
    </CoreActionForm>
  );
}

Best Practices

  1. Use Controlled Mode - Prefer controlled mode with useForm hook for better type safety and programmatic control
  2. Leverage getParams - Use getParams for dynamic data that changes at execution time
  3. Use params for Static Data - Use params prop for static data that doesn’t change
  4. Handle Errors Gracefully - Use onError and onValidationError callbacks for custom error handling
  5. Model Binding - Pass model IDs and let the backend resolve models automatically
  6. Confirmation Dialogs - Use confirm: true on actions that are destructive or irreversible
  7. Type Safety - Use TypeScript to ensure form data matches action parameter types

See Also