Skip to main content
CoreModal and CoreModalPage wrap InertiaUI Modal with server-side configuration, automatic title/description injection, and a unified API for both dialogs and sheets.
Use modals for focused interactions that interrupt the current workflow, and sheets for non-modal side panels that keep the main page context visible.

CoreModalPage (Server-Side)

Return CoreModalPage from a controller to configure the modal from the backend. The framework automatically validates the x-inertiaui-modal header and returns a 404 if it’s missing.
1

Create a modal route

Return CoreModalPage from your controller with a component name and configuration:
use Inly\Core\Pages\CoreModalPage;
use Inly\Core\Enums\SheetPosition;

public function create()
{
    return CoreModalPage::make('users/create')
        ->title(__('New user'))
        ->description(__('Create a new user account'))
        ->sheet()
        ->position(SheetPosition::LEFT)
        ->with(['roles' => $roles]);
}
The modal now displays as a left-aligned sheet with the specified title, description, and roles data.
2

Support both modal and page requests (optional)

If a route must support both modal and full-page requests, check the header explicitly:
public function create()
{
    if (request()->header('x-inertiaui-modal')) {
        return CoreModalPage::make('users/create')
            ->title(__('New user'))
            ->sheet()
            ->with(['roles' => $roles]);
    }

    return CoreShowPage::make('users/create')
        ->title(__('New user'))
        ->with(['roles' => $roles]);
}
This pattern allows the same route to render as a modal overlay or a full page depending on how it’s accessed.

CoreModalPage API

make(string $component)
method
required
Create a new modal page instance. Pass the component path relative to resources/js/Pages/.
title(?string $title)
method
Set the modal’s displayed title. Use the __() helper for translatable strings.
description(?string $description)
method
Set the modal’s description text shown below the title.
sheet(bool $sheet = true, SheetPosition|null $position = null)
method
Enable sheet (slideover) mode. Sheets appear as side panels instead of centered dialogs. Optionally set the position in the same call.
position(SheetPosition|string $position)
method
Set the sheet position (only works when sheet() is enabled). Available positions: RIGHT (default), LEFT, TOP, BOTTOM.
with(array $data)
method
Pass additional props to the component. All data is available as component props.
fromDomainObject($domainObject)
method
Auto-configure modal properties from a domain object. Useful for operations like edit forms.

Opening Modals from React

Use CoreModalLink to trigger a modal when users click a button or link:
import { CoreModalLink } from '@/core/components/core-modal';

<CoreModalLink href={route('users.create')} modalClassName="max-w-sm">
  <Button>{__('New User')}</Button>
</CoreModalLink>
The modalClassName prop accepts Tailwind width classes like max-w-sm, max-w-md, or max-w-2xl to control the modal’s maximum width.

Programmatically

Open modals in response to user actions or events:
import { useModalStack } from '@inertiaui/modal-react';

const { visitModal } = useModalStack();

// Open as a centered dialog
visitModal(route('users.create'));

// Open as a right-aligned sheet
visitModal(route('filters.panel'), { 
  slideover: true, 
  position: 'right' 
});

CoreModal (React Component)

Use CoreModal to wrap your modal content. It automatically reads configuration from pageData (set by CoreModalPage on the server), and lets you override settings with props.
1

Render basic modal

CoreModal automatically applies server-side configuration:
import { CoreModal } from '@/core/components/core-modal';

export default function UserCreate({ roles }) {
  return (
    <CoreModal>
      <UserForm roles={roles} />
    </CoreModal>
  );
}
2

Override server configuration (optional)

Props override pageData values when both are present:
<CoreModal
  coreDialogProps={{
    title: __('Custom Title'),
    description: __('Custom description'),
  }}
>
  {children}
</CoreModal>
3

Access modal context (optional)

Use function children to access modal state and controls:
<CoreModal>
  {({ close, reload, isOpen }) => (
    <>
      <p>{isOpen ? __('Open') : __('Closed')}</p>
      <Button onClick={close}>{__('Close')}</Button>
    </>
  )}
</CoreModal>
The context provides:
  • close() — Close the modal
  • reload() — Refresh modal data
  • isOpen — Current open state

CoreModal Props

className?
prop
default:"undefined"
Tailwind max-width class to constrain modal width (e.g., max-w-sm, max-w-md).
coreDialogProps?
prop
default:"{}"
Override dialog mode properties including title, description, and footer.
coreSheetProps?
prop
default:"{}"
Override sheet mode properties including side (sheet alignment).

CoreTableModal for Bulk Operations

Use CoreTableModal when your modal operates on table rows. It automatically merges IDs from individual row actions and bulk selections, so your components only receive a unified ids array.
1

Set up table modal

Attach the modal to your table’s modals prop:
import { CoreTableModal } from '@/core/components/core-modal';

<InertiaTable
  resource={usersTable}
  modals={({ actions }) => (
    <CoreTableModal
      name="update-user-status"
      selectedItems={actions.selectedItems}
      coreDialogProps={{
        title: __('Update status'),
        description: __('Update the status for selected users'),
      }}
    >
      {({ ids, close }) => (
        <UpdateUserStatusDialog ids={ids} close={close} />
      )}
    </CoreTableModal>
  )}
/>
2

Use merged IDs in your dialog

The ids array automatically includes IDs from either individual row actions or bulk selections:
function UpdateUserStatusDialog({ ids, close }) {
  const handleSubmit = async (status) => {
    await router.put(route('users.update-status'), { ids, status });
    close();
  };

  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      handleSubmit(e.target.status.value);
    }}>
      {/* form fields */}
    </form>
  );
}

CoreTableModal Props

name
prop
required
Unique identifier for the modal instance. Used internally to track modal state.
selectedItems
prop
required
Array of selected item IDs from bulk actions. Passed from the table’s action context.
children
prop
required
Render function receiving { ids, close }. ids is the merged array of IDs; close() dismisses the modal.
coreDialogProps?
prop
Override dialog title, description, and other properties.

ID Merging Behavior

CoreTableModal applies this logic:
  1. If a rowActionId exists (user clicked a row action), use [rowActionId]
  2. Otherwise, use the selectedItems array
  3. Reset rowActionId when the modal closes
This ensures bulk operations work seamlessly whether users select multiple rows or click a single-row action menu.
Use the WithTableQuery trait in your Form Request to handle both single-item (route parameter) and bulk (ids array) updates. See the Data Tables documentation for details.

Choosing the Right Modal Component

Use CaseComponent
Route-driven modal with server-side configurationCoreModalPage + CoreModal
Click to open modalCoreModalLink
Programmatic modal openinguseModalStack()
Table row operationsCoreTableModal
Local state modal (not route-driven)CoreDialog or CoreSheet

Troubleshooting

Always call sheet() before position() when configuring sheet modals in CoreModalPage.
Verify that selectedItems is passed from the table’s action context and that name is unique for each modal on the page.
The Core API uses “sheet” terminology, but this maps to InertiaUI’s “slideover” internally. Both refer to the same side panel component.

Advanced Usage

For local state modals that don’t need routing or server configuration, use CoreDialog or CoreSheet directly:
import { CoreDialog, CoreSheet } from '@/core/components/core-modal';

// Local dialog
<CoreDialog open={isOpen} onOpenChange={setIsOpen}>
  {/* content */}
</CoreDialog>

// Local sheet
<CoreSheet open={isOpen} onOpenChange={setIsOpen} position="right">
  {/* content */}
</CoreSheet>