List
Description
The "List" module purpose - render list of some records from your database to work with them.
When the resolved list has no rows, Adminix renders a single No records found. table row. The empty state uses the same table width and responsive scroll container as regular records, including relation manager child lists.
Configuration
Basic example:
To add to the page List module you need paste AlexKudrya\Adminix\Modules\List\ListModule class instance as an argument to addModule or addModules method of AdminixPage object.
ListModule configuration
Full settings list:
Method | Description | |
|---|---|---|
name | Name of module, must be unique in current page.
name('users')
| Required |
title | Title on the top of the module
title('USERS')
| Optional |
pagination | Number of items per page. To disable pagination must be equal to 0 or not exist.
pagination(20)
| Optional |
dataSource | Source of data for List, can be an
dataSource(\App\Models\User::class)
| Required |
withDeleted | Determines render or not deleted records (working with models with included By default, is not active
withDeleted()
| Optional |
restoreAction forceDeleteAction softDeleteActions | Render built-in restore and/or permanent delete controls for trashed Eloquent
softDeleteActions()
Detailed settings described below. #Soft-delete row actions | Optional |
cloneAction bulkCloneAction cloneActions | Render built-in row clone and/or bulk clone controls for SQL/Eloquent list records.
cloneActions()
Detailed settings described below. #Clone actions | Optional |
hiddenOnIndex / hideOnIndex | Removes the field from the server-rendered index table and from inline-edit writable field resolution. Use this for server-side list visibility, not for browser-only column preferences.
hiddenOnIndex()
hideOnIndex()
| Optional |
editable | Determines possibility to edit records directly in the List. Editable possibility for every field determines in his personal config. By default, is not active
editable(),
| Optional |
primaryKey | Primary key in source table. Used for editable mode. Usually it is "id"
primaryKey('id')
| Required if editable mode is enabled |
criteria | Database
criteria([
['is_admin', '=', false]
])
Or you can paste parameter from route:
criteria([
['user_id', '=', 'param:1']
])
For example, for Module/query criteria are normalized through Adminix's shared criteria contract. Supported operators are | Optional |
resource | Laravel
resource(\App\Http\Resources\UserAdminResource::class)
| Optional |
addLens addLenses | Add server-defined saved views for the current list. Each lens can apply extra criteria, lens-specific default sorting, and explicit
addLenses(
ListLens::name('active')
->title('Active')
->criteria(['status' => 'active']),
ListLens::name('trash')
->title('Trash')
->withDeleted()
)
Detailed settings described below. #Lenses | Optional |
addQuickFilter addQuickFilters | Add server-defined quick presets that reuse configured list filters, search, and lenses. The active quick filter is selected by
addQuickFilter(
ListQuickFilter::make('active')
->title('Active')
->filter('status', 'active')
)
Detailed settings described below. #Quick filters and chips | Optional |
addField addFields | Methods by which you can add fields to List for displaying. Arguments can be only Detailed here #Fields
addField(
ListField::name('Name')
->field('name')
->type(ListFieldTypeEnum::STRING)
->sortable()
)
| Required |
rowDetails | Attach a server-declared expandable detail panel to each row. The panel reuses
rowDetails(
ListRowDetails::make('Details')
->columns(2)
->addFields(
DetailField::make('Status', 'status')->badge('#754195'),
DetailField::make('Payload', 'metadata')->json()
)
)
Detailed settings described below. #Row details | Optional |
search | Render "Search" input on the top of module. Argument -
search(
ListSearch::searchFields(['name', 'email'])
->placeholder('Search users')
)
Detailed settings described below. #Searching | Optional |
addFilter addFilters | Methods by which you can add filters for current List. Arguments can be only several classes instances:
->addFilters(
ListFilter::field('role_id')
->name('Role')
->src(
InputSelectSrc::nameField('name')
->valueField('id')
->dataSource(UserRole::class)
),
ListDateRangeFilter::field('created_at')
->name('Registration time')
->withTime()
)
Detailed settings described below. #Filters | Optional |
columnVisibility | Add a
columnVisibility()
Detailed settings described below. #Column visibility | Optional |
densitySwitcher | Add table density controls to the list settings menu. The switcher is browser-only and stores compact/comfortable state by list name.
densitySwitcher()
Detailed settings described below. #Density and sticky table controls | Optional |
stickyHeader | Keep the table header visible while scrolling inside the table container.
stickyHeader()
Detailed settings described below. #Density and sticky table controls | Optional |
stickyActions | Keep the row action column visible while scrolling wide tables horizontally. It uses the existing row action buttons/forms/modal togglers.
stickyActions()
Detailed settings described below. #Density and sticky table controls | Optional |
rowActionsDropdown | Render row actions behind one vertical three-dots dropdown button instead of inline buttons. This is only a presentation option; row action definitions, criteria, methods, params, CSRF forms, tooltips, and modal togglers stay server-defined.
rowActionsDropdown()
Detailed settings described below. #Actions | Optional |
savedTableViews savedViews | Add a
savedTableViews()
Detailed settings described below. #Saved table views | Optional |
resetControls | Add an opt-in "Reset view" action to the list settings menu for the current list state. The control removes current list search, quick filter, filters, sorting, active lens, and pagination from the URL.
resetControls()
Detailed settings described below. #Reset controls | Optional |
addAction addActions | Methods by which you can add action buttons (Links) for every item in current List module. Arguments can be only several classes instances:
addAction(
AdminixLinkModule::name('user_details')
->title('DETAILS')
->icon('bi bi-pencil')
->uri(UserPage::URI)
->params(['record:id']),
)
Detailed settings described below. #Actions | Optional |
addBulkAction addBulkActions | Methods by which you can add server-side batch actions for selected rows in current List module. Arguments can be only
addBulkAction(
BulkAction::make('mark_paid')
->title('Mark paid')
->icon('bi bi-check2')
->confirm('Mark selected orders as paid?')
->handler(MarkOrdersPaidAction::class)
)
Detailed settings described below. #Bulk actions | Optional |
sorting | Basic sorting for records in current List module. Argument can be only Example:
sorting(
Sorting::field('id')
->direction('asc')
)
| Optional |
groupBy ungrouped | Renders server-owned group header rows for contiguous records with the same configured field value. Use normal
groupBy('status', 'Status')
ungrouped()
| Optional |
newItemLink | Setting for "New Item" button rendered between Title and List, near to Search input. When a list has both create and search controls, Adminix keeps them grouped on the left side of the list toolbar. Format is similar to Adminix Link module.
newItemLink(
AdminixLinkModule::name('new_user_btn')
->title('NEW USER')
->icon('bi bi-person-fill-add')
->uri(NewUserPage::URI)
)
Detailed here #New item link | Optional |
Lenses
Lenses are server-defined saved views for ListModule. They are useful for common list presets such as Active, Drafts, Deleted, VIP, or Needs review.
The active lens is selected by the query key lens-{moduleName}. Adminix resolves this value only against lenses configured in PHP. Unknown lens names are ignored and the default list is rendered.
Lens criteria are applied after base ListModule::criteria() and before user filters, search, and sorting. Lens filters are defaults for configured list filters. Use the filter field as the key:
Result: when the URL contains lens-users=needs-review and does not contain filter-users-status, Adminix applies status = pending and shows the filter as selected. If the URL already contains filter-users-status, the request value wins and the lens default is not used. Date range defaults can use a nested from/to map or flat keys such as created_at-from. Lens sorting is a default sorting only: if the request contains a valid sort-{module}-{field} parameter, the user-selected sorting wins. Pagination links keep the active lens query parameter.
Use base criteria() for mandatory tenant/user scoping. Use lenses only for alternate server-owned views inside that scope.
Quick filters and chips
Quick filters are small server-defined shortcuts for frequent list states. They do not introduce a separate query language. Each quick filter can set defaults for configured list filters, the list search query, and an existing lens.
The active quick filter is selected by the query key quick-{moduleName}. Adminix resolves this value only against ListQuickFilter instances configured in PHP. Unknown quick filter names are ignored.
Filter defaults use the same keys as ListLens::filters(). For date ranges, call range($field, $from, $to) or pass ['created_at' => ['from' => '2026-01-01', 'to' => '2026-01-31']] to filters().
Precedence is:
explicit request search/filter/lens values;
active quick filter defaults;
active lens default filters.
Result: clicking a quick filter renders the preset button as active, applies only server-configured filters/search/lens, and shows removable chips for the active quick filter, lens, search, and filter values. When a chip represents an explicit URL parameter, its remove link deletes that parameter. When a value came from a quick filter or lens default, the remove link clears the preset source instead.
Saved table views can persist quick-{moduleName} only when it references a configured quick filter. resetControls() clears active quick filters along with search, filters, lens, sorting, and pagination. Reorder handles remain disabled while a quick filter is active, because the visible rows are filtered.
Use quick filters for operator convenience, not tenant or authorization boundaries. Mandatory scoping still belongs in criteria(), policies, guards, relation context, or app-side query criteria. adminix:doctor warns when a quick filter references missing filter fields, missing lenses, invalid filter values, or has no effect.
Fields
Fields is an array, of fields to be rendered in the List.
To add Field to the List module you need paste AlexKudrya\Adminix\Modules\List\ListField class instance as an argument to addField or addFields method of ListModule object.
Field Presentation
ListField supports the same presentation helpers as resource fields for table columns and inline-edit controls. These helpers only change server-rendered UI; they do not change query criteria, sorting, writable fields, or export behavior.
Available helpers:
placeholder($text)sets inline-edit input placeholders and async select search placeholders;help($text)renders escaped helper text in the table header;width('narrow'|'medium'|'wide'|'full')adds safe width classes to the table header and cells;narrowWidth(),mediumWidth(),wideWidth(), andfullWidth()are named width shortcuts;alignment('start'|'center'|'end')controls table header/cell alignment;left,middle, andrightare normalized aliases;alignStart(),alignCenter(), andalignEnd()are named alignment shortcuts.
Invalid width/alignment values are ignored. Use hiddenOnIndex(), hideable(), defaultHidden(), editable(), and exportable() for behavior; presentation helpers are not access-control tools.
Row details
Use ListRowDetails when a list needs a compact primary row plus expandable read-only metadata. Adminix renders one toggle column and a hidden detail row for every visible record. The detail panel is prepared from the same server-side list datasource, base criteria, active lens, filters, search, sorting, pagination, and optional relation scope used for the rendered table.
ListRowDetails supports:
make($title = null)/title()for an optional detail heading;columns(1..4)for the read-only field grid;addField()/addFields()withDetailFieldor anotherResourceFieldsubclass;expanded()to render all detail rows initially open, otherwise they are collapsed and toggled with JavaScript.
Row details do not add a write endpoint and do not trust browser-owned datasource, criteria, field names, or primary keys. Password fields are rendered without stored values, matching resource/detail password safety. Keep sensitive tenant, owner, credential, and internal audit columns out of row details unless they are intentionally visible to the admin user.
Badge / Status Field
Use badge() or status() when a list column should show a compact visual state instead of raw text. Badge fields are display-only: even on editable lists, ListFieldTypeEnum::BADGE is excluded from inline edit writes.
badge('#754195') applies one color to all values. badgeMap() accepts the same map as status(). When a value is not mapped, Adminix renders the scalar value with the field default badge color.
Money / Currency Field
Use money($currency = 'USD', $decimals = 2) for amount columns that should render as tabular currency values. On editable lists, money fields remain writable number inputs and use a decimal step derived from $decimals.
Result: read-only cells render values such as EUR 1,234.50; editable cells render a number input with step="0.01".
Color Field
Use color() for color values stored as CSS color strings, commonly hex values such as #754195. Read-only list cells render a swatch and the stored value; editable list cells render a native color picker.
Slug Field
Use slug() for columns that store URL-friendly slugs. List slug fields render like scalar text and can still be sortable when the underlying database column supports it.
If the list is editable and the field is marked editable(), Adminix renders a normal text input. Keep slug validation and uniqueness server-side in the resource or inline-edit validation rules.
Markdown Field
Use markdown() for columns that store markdown text. Read-only list cells render a compact safe preview: raw HTML is escaped, while simple markdown links, **strong**, *emphasis*, inline code, and line breaks are rendered. Editable list cells render a textarea and submit the raw markdown value.
Markdown columns are usually long, so generated smart resources mark them defaultHidden() and do not make them sortable. Keep validation and content policy server-side.
JSON / Code Field
Use json() for JSON payload columns. Read-only list cells render a compact pretty-printed preview, and editable list cells render a monospace textarea with progressive JSON syntax feedback. The helper adds the Laravel json validation rule; if JavaScript is unavailable, inline edit still submits a normal textarea value to server validation.
Generated smart resources mark JSON list columns defaultHidden() and do not make them sortable. Keep schema-specific validation in the consuming application.
Key-Value Field
Use keyValue() for object-like JSON columns such as metadata, settings, options, or attributes. Read-only list cells render compact key/value rows. Editable list cells render the same progressive key/value editor as resource forms and submit a single JSON object field.
The helper adds the Laravel json validation rule. Generated smart resources mark key-value list columns defaultHidden() and do not make them sortable.
Date Range Field
Use dateRange() for JSON values shaped like {"from":"2026-01-01","to":"2026-01-31"}. Read-only list cells render a compact date-range label, and editable list cells render the same progressive two-date editor as resource forms.
The helper adds the Laravel json validation rule. Keep ordering and filtering on dedicated date columns when the database needs efficient range queries; this field is for displaying or editing a persisted range value.
Timezone Field
Use timezone() for columns that store IANA timezone identifiers. Read-only list cells render the stored identifier, and editable list cells render the same server-declared timezone select as resource forms.
The helper adds Laravel's timezone validation rule for editable list submissions. Use application validation when timezone changes must be restricted by tenant, user, or business context.
Tags Field
Use tags($separator = ',') for tag-list columns. Read-only list cells render chips. Editable list cells render a normal text input with a progressive chip preview and submit the separator-delimited string.
The formatter accepts separator-delimited strings, JSON array strings, and array values. Generated smart resources mark tag columns defaultHidden() and do not make them sortable.
Example:
ListField configurations
Method | Description | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
name | Title of the field displayed on the top of table (inside
name('Role')
| Required | ||||||||
type | Type of rendered field, can be provided only by Available types for rendered field:
It determines data format to render, and type of input for Example:
type(ListFieldTypeEnum::SELECT)
| Required | ||||||||
field | Name of field in database or resource (if resource was provided).
field('role_id')
| Required | ||||||||
sortable | Determines the sorting possibility for current field. Can not be used to fields which provided by resource, and not exist in database table, or his name was modified. By default, is not enabled.
sortable()
| Optional | ||||||||
hideable | Allows the field to appear in the
hideable()
| Optional | ||||||||
defaultHidden | Allows the field to appear in the
defaultHidden()
| Optional | ||||||||
pinned pinnedLeft pinnedRight unpinned | Sets the default sticky side for a rendered index column. Pinned columns are still browser-side presentation state; they do not change datasource, criteria, sorting, export, or authorization. Hidden/internal and hidden-on-index fields cannot be pinned.
pinnedLeft()
pinnedRight()
unpinned()
| Optional | ||||||||
sum avg average min max countSummary summarize summaryLabel | Adds a server-computed footer summary for a rendered list column. The aggregate uses the current datasource, criteria, active lens, filters, search, and sorting before pagination. Hidden/internal and hidden-on-index fields cannot render footer totals.
sum('Total')
avg('Average')
countSummary('Rows')
summaryLabel('Totals')
| Optional | ||||||||
editable | Determines the editable possibility for current field. Can not be used to fields which provided by resource, and not exist in database table, or his name was modified. This will not be working if in To working correctly requires primary key (usually it is "id" field) in this fields list (can be By default, is not enabled.
editable()
| Optional | ||||||||
src | Required for fields with type For example, Argument can be only Example:
ListField::name('Role')
->field('role_id')
->type(ListFieldTypeEnum::SELECT)
->editable()
->src(
InputSelectSrc::nameField('name')
->valueField('id')
->dataSource(Role::class)
)
| |||||||||
asyncSrc | Use Example:
use AlexKudrya\Adminix\Modules\AsyncInputSelectSrc;
ListField::name('Author')
->field('author_id')
->type(ListFieldTypeEnum::SELECT)
->editable()
->asyncSrc(
AsyncInputSelectSrc::dataSource(User::class)
->nameField('name')
->valueField('id')
->searchFields(['name', 'email'])
->criteria(['active' => true])
->minLength(2)
->limit(20)
->placeholder('Search authors')
);
Result: editable rows search remote options with For read-only list cells, async fields render the stored value because Adminix intentionally does not eager-load labels for all rows. Use synchronous | |||||||||
addSelectRecords | Required for fields with type Arguments can be only Example:
ListField::name('Role')
->field('role_id')
->type(ListFieldTypeEnum::SELECT)
->editable()
->addSelectRecords(
SelectRecord::name('Admin')->value(1),
SelectRecord::name('Manager')->value(2),
SelectRecord::name('Seller')->value(3),
SelectRecord::name('Guest')->value(4),
)
...
|
Row reorder
reorderable($orderField) enables flat drag-and-drop ordering for a ListModule. It renders a left-side grip handle, disables user-driven column sorting for that list, and uses the configured base Sorting as the canonical row order. If the module does not already have sorting, reorderable('sort_order') configures Sorting::field('sort_order')->direction('asc') automatically.
Result: Adminix shows a grip column for reorderable rows only when the whole current reorder scope is visible. Drag the grip with a pointer or focus it and use ArrowUp/ArrowDown. The browser sends only the ordered primary keys and a signed context; Adminix resolves page, module, datasource, criteria, primary key, order field, and relation scope from server-side configuration.
Reorder handles are intentionally hidden when the visible scope cannot be written predictably:
the list has pagination enabled;
the URL contains current-list search, quick filter, filters, lens, sort, or page state;
the primary key is not rendered in the list data;
fewer than two rows are visible;
the module does not have one ascending base sort by the reorder field.
in tree mode, a visible row points to a parent outside the current visible row set.
For standard Eloquent and query-builder data sources, the built-in writer uses a transaction and keeps the existing order-value set. It moves one row to a temporary sentinel value outside the current datasource range, rotates the affected cycle, and then assigns the existing values to the new row order. This avoids immediate unique-index collisions on databases that do not support deferrable unique constraints, while also avoiding permanent growth of the order values after repeated reorders. The current scope must have unique numeric order values.
Adminix does not automatically normalize order values to 1..N. Normalization can collide with unknown global or composite unique constraints, tenant scopes, category scopes, or non-SQL storage rules. If an application wants periodic normalization, implement it in application code where the full uniqueness scope and storage semantics are known.
Use treeReorderable($orderField, $parentField = 'parent_id', $rootParentValue = null) for adjacency-list trees. Adminix renders the current scoped rows in server-side tree preorder, signs both the rendered primary keys and their parent map, and sends adminix_reorder_parent_ids only for tree mode. Pointer drag reorders rows inside the same parent; keyboard ArrowRight indents a row under the previous sibling, ArrowLeft outdents it one level, and ArrowUp/ArrowDown move it among siblings. Tree reorder controls are hidden when the current scope is an incomplete tree and a visible row's parent is outside the rendered row set, so saving cannot detach hidden parents.
For the built-in SQL/Eloquent writer, tree reorder updates the configured parent field and normalizes sort_order inside each sibling group. Use the third argument when root rows use a sentinel such as 0 instead of NULL: treeReorderable('sort_order', 'parent_id', 0). Use reorderUsing() when your tree uses materialized paths, nested sets, fractional ranks, custom locks, or storage-specific constraints.
Use authorizeReorderUsing() to add a module-level authorization guard:
Use reorderUsing() for MongoDB, external services, custom rank algorithms, application-side locks, version checks, or any storage where the built-in SQL/Eloquent writer should not decide persistence. Adminix still validates the signed rendered row set before calling the handler.
ListReorderRequest exposes:
module()for the server-sideListModule;previousIds()for the signed row order that was rendered;orderedIds()for the submitted permutation;parentIds()for the submitted tree parent map, empty for flat reorder;params()for signed page parameters after Adminix param-index compatibility resolution;relationContext()for signed relation child-list context, when present;httpRequest()for the original Laravel request.
adminix:doctor warns when reorderable lists miss a primary key field, base order field, valid ascending sorting, or use pagination. For the built-in writer it also checks that the reorder field exists in the SQL datasource, and tree reorder checks the configured parent field.
Column visibility
columnVisibility() adds a Columns section to the compact list settings dropdown for the current ListModule. Only fields marked with hideable() or defaultHidden() are included in that section. When the list uses columnVisibility() or savedTableViews(), the same section also exposes server-declared visible fields for browser-side column order, width, and pinned-side controls. Hidden fields, row reorder handles, row detail toggles, bulk checkboxes, inline edit buttons, and row action service columns are not part of the configurable column layout. The list settings dropdown grows to its content height and only uses vertical scrolling when the menu would exceed the viewport. On narrower desktop viewports, the list settings button shifts left only when its screen position vertically overlaps the fixed shell notification bell. Column toggles, density options, reset, and CSV export actions share the same menu item sizing, spacing, rounded corners, and hover treatment. Column toggles are rendered as switch controls, matching boolean field controls elsewhere in Adminix.
Result: Adminix shows a gear-icon settings button as a root-level absolute control aligned to the list title area. The control does not reserve toolbar or filter space; when a list has no title, it stays inside the list root without adding an empty row. The Customer column is visible by default and can be hidden by the user. The Internal note column is hidden by default and can be shown from the same settings menu. Visible list columns can be moved left/right, assigned an auto, narrow, medium, wide, or full width preference, and pinned left or right. pinnedLeft() and pinnedRight() set the default sticky side; clicking the same pin button in the settings menu unpins the column for that browser or saved view. The hidden id field and service columns for inline edit or row actions are not controlled by column visibility.
Column visibility, column order, column width, and column pinning are browser-side presentation settings. They are stored in local storage by list name and do not change request keys, datasource, criteria, filters, sorting, row actions, authorization, or export scope. When savedTableViews() is enabled, named table view presets can also persist normalized column order, width, and pin state through the configured table view provider. For server-side list visibility, use ListField::hiddenOnIndex()/hideOnIndex(). Hidden-on-index fields are not rendered in the table and are not writable through inline edit. adminix:doctor warns when a hidden/internal or hidden-on-index list field is marked pinned, because it cannot render as a sticky table column.
Table summaries
Use field-level summary helpers when a list needs footer totals aligned with its rendered columns. Adminix computes summary values server-side from the same list query scope used for the visible records: datasource, base criteria, active lens, filters, search, and sorting are applied, but pagination is not.
Result: Adminix renders a <tfoot> row below the list body. The footer uses the same data-adminix-list-column contract as header and body cells, so browser-side column visibility, order, width, reset, saved table views, and pinned columns keep footer cells aligned.
Available helpers:
sum(?string $label = null);avg(?string $label = null)/average(?string $label = null);min(?string $label = null);max(?string $label = null);countSummary(?string $label = null);summarize(ListSummaryAggregateEnum|string $aggregate, ?string $label = null);summaryLabel(?string $label).
Money fields format sum, avg, min, and max with the configured currency and decimals. countSummary() renders as a plain number even when used on a money field.
Summaries are display-only and do not change list datasource, filters, sorting, export, row actions, authorization, or writable fields. adminix:doctor warns when a summary is configured for a hidden/internal or hidden-on-index field, or when the summary field does not exist in a known SQL/Eloquent datasource table.
Grouped rows
Use groupBy($field, $label = null) when a list needs visual group headers between rows. Adminix groups the already prepared rows for the current rendered page, so datasource, criteria, active lens, filters, search, sorting, pagination, row actions, exports, and writes keep their normal server-side behavior.
Result: Adminix inserts full-width group header rows inside the table body before each contiguous group. The group field must be present in the ListField collection so Adminix can prepare its row value; it may be hidden from the index table when the value should appear only in the group header. groupBy() does not reorder records by itself. Configure sorting() by the same field, or use a lens/default query order, when groups should be contiguous.
Use ungrouped() to clear a previously configured grouping helper in composed module factories. adminix:doctor warns when groupBy() references a field that is not configured in the list or is missing from a known SQL/Eloquent datasource table.
Density and sticky table controls
densitySwitcher(), stickyHeader(), and stickyActions() are opt-in presentation helpers for wide or frequently used tables.
Result: Adminix renders Comfortable and Compact density choices inside the gear-icon list settings menu. The selected density is stored in browser local storage by list name. Compact density reduces table cell padding plus inline row controls such as editable inputs, selects, row action buttons, switches, date-range/key-value editors, async selects, and row images. Inline controls use a 32px minimum height, row images cap at 38px, cells align content vertically in the middle, and action button icons keep the normal UI-kit gap from text. stickyHeader() keeps table headers visible inside the scroll container. stickyActions() keeps the existing final service column visible while the table fits the available width. When a list table is wider than the available viewport, Adminix preserves the table's content width, moves the service column into the horizontal scroll flow, and uses horizontal scrolling instead of compressing or overlapping right-side columns into data cells.
These controls do not create a new action layer. Existing addAction()/addActions() links, non-GET forms, confirm prompts, tooltips, modal togglers, and record:*/param:* route parameters continue to render through the same row action contract. If the list also uses rowActionsDropdown(), stickyActions() keeps the dropdown trigger in the same service column.
CSV export
exportCsv() adds an Export CSV action to the list settings menu for the current ListModule. CSV export is disabled by default and must be enabled per list.
Result: Adminix shows an Export CSV action inside the gear-icon list settings menu. The endpoint applies the same server-side datasource, criteria, active lens, filters, search, and sorting as the list. The current pagination page is ignored, and export rows are capped by the configured limit. The default limit is 10000.
The browser cannot choose a datasource or arbitrary columns for CSV export. Adminix signs the current page params when rendering the export link so param:* criteria use the same route context as the visible list. ListFieldTypeEnum::HIDDEN fields are excluded by default. Use ListField::exportable() only for hidden fields that are safe to include in the CSV. Fields hidden with hiddenOnIndex() are also excluded unless they are explicitly marked exportable(). Row actions, inline edit controls, service columns, and UI-only column visibility are not exported.
Bulk actions
Bulk actions are opt-in server-side actions for selected rows in a ListModule. They render a checkbox column, Select all current page checkbox, action dropdown, selected count, and submit button above the table.
Result: Adminix shows bulk controls above the list. The button stays disabled until the user selects at least one current-page row and one configured action. When confirm() is configured, the browser shows that confirmation before submitting. The web endpoint redirects back with a success or error toast, or returns a direct Response when the handler returns one.
BulkAction settings:
name()is the stable server-side action key submitted by the browser.title()is the dropdown label.icon()stores an optional Bootstrap icon class for package consumers and future renderers.tooltip()stores optional helper text for package consumers and future renderers.color()stores an optionalColorsEnumvalue or CSS color string.destructive()marks dangerous actions for custom renderers and future UI states.confirm()sets an optional submit confirmation message.criteria()stores optional action visibility criteria metadata; execution still uses the selected row scope.addField()/addFields()declare input fields shown before the selected bulk action is submitted.handler()accepts a callable, object, or class string. ImplementBulkActionHandlerInterfacefor reusable actions.authorizeUsing()/authorization()accepts a boolean, callable, object withauthorize(), or class string.batchLimit()caps selected scalar IDs before the handler runs. Default is1000.queued('Message')dispatches the handler through Laravel queues after the normal server-side scope and authorization checks.progressTrigger()starts a matchingProgressBarModuletrigger when the selected action is submitted.onConnection()andonQueue()optionally select the Laravel queue transport for queued bulk actions.actionMetadata()returns the shared Actions metadata DTO.
BulkActionRequest gives handlers:
module()andaction()from server-side configuration;requestedIds()from the request after scalar normalization;selectedIds()after applying the current list scope;params()after signed page-param context verification and legacy index normalization;query()already scoped to selected records;primaryKey()from the list module;httpRequest()for request/session context when needed.fields()andfield($name, $default = null)for server-declared action field values.
Action fields are documented in Actions. They are submitted as adminix_action_fields[...], but Adminix only passes keys declared on the selected server-side BulkAction to the handler.
Handlers can return:
BulkActionResult::success('Message');BulkActionResult::error('Message');BulkActionResult::validation(['field' => ['Message']]);BulkActionResult::redirect('/adminix/orders');BulkActionResult::openInNewTab('/adminix/orders/export.csv');BulkActionResult::modal('Title', 'Body');BulkActionResult::download($response)or a Symfony/LaravelResponsefor downloadable/custom responses.
Typed action responses are documented in Actions. Queued actions are documented in Actions.
The endpoint resolves page, module, relation context, and action from server-side module configuration. The browser submits only selected IDs and action name. Datasource, primary key, criteria, active lens, filters, search, sorting, relation parent scope, and page params are resolved server-side. IDs outside the current criteria/search/filter/relation scope are ignored; if no selected ID remains in scope, the request fails with a controlled validation error. After a handler returns a normalized result, Adminix dispatches BulkActionExecuted and passes the scope-filtered selected IDs in audit metadata. See Audit for recorder and listener examples.
Bulk actions do not replace row actions. Existing addAction()/addActions() links, non-GET row forms, modal togglers, confirm prompts, tooltips, and record:* params remain the row action contract.
Searching
Determines possibility to make full-text search in source table.
Example:
Method | Description | |
|---|---|---|
searchFields | An array of fields which involved in the search process. The more fields, the slower but more accurate the search. Every field must be existed in main database table
searchFields(['name', 'email'])
| Required |
placeholder | A placeholder for Search
placeholder('Search users')
| Optional |
Filters
Add filters to the top of the module, near to search input. Provides the ability to filter the list by specific fields.
There are several types of filters available:
ListFilter -
AlexKudrya\Adminix\Modules\List\ListFilterListDateFilter -
AlexKudrya\Adminix\Modules\List\ListDateFilterListDateRangeFilter -
AlexKudrya\Adminix\Modules\List\ListDateRangeFilter
ListFilter
Basic filter - <select/> input with defined options
Example:
1 usage variant - get records for filter from DB table by Eloquent model or table name, and took nameField as displayable name and valueField as value for filtering
Or 2 usage variant - get records for filter from DB table by Eloquent model or table name by SELECT DISTINCT ... query
Or 3 usage variant - set records for filter manually as list of ListFilterRecord objects
Method | Description | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
name | Title of the filter
name('Role')
| Required | ||||||||
field | Name of the field used for filtering. Field must be existed in base database table
field('role_id')
| Required | ||||||||
src | Required if filter_records is empty. Has a format similar to
src(
InputSelectSrc::nameField('name')
->valueField('id')
->dataSource(\App\Models\Role::class)
),
| Required if no filterRecords() and no distinct() | ||||||||
distinct | Field and table for `SELECT DISTINCT` selection
distinct('role', \App\Models\User::class),
| **Required** if no **src()** and no **filterRecords()** | ||||||||
filterRecords | Required if src is empty. It is an
addFilterRecords(
ListFilterRecord::name('Client')->value(1),
ListFilterRecord::name('Guest')->value(2),
ListFilterRecord::name('Manager')->value(3),
ListFilterRecord::name('Admin')->value(4),
)
| Required if no src() and no distinct() |
ListDateFilter
Date filter - allows you to select records within a selected date.
Example:
Method | Description | |
|---|---|---|
field | Name of the field used for filtering. Field must have
field('created_at')
| Required |
name | Title of the filter
name('Registration date')
| Required |
ListDateRangeFilter
Date range filter - allows you to select records within a date range.
Example:
Method | Description | |
|---|---|---|
field | Name of the field used for filtering. Field must have
field('created_at')
| Required |
name | Title of the filter
name('Registration date')
| Required |
withTime | Determines whether the filter will filter by time or only by dates. By default, filter only by date, without time.
withTime()
| Optional |
filterFrom | Define "From" input. If set By default, it is enabled.
filterFrom(false)
| Optional |
filterTo | Define "To" input. If set By default, it is enabled.
filterTo(false)
| Optional |
Saved table views
savedTableViews() enables user-owned table view presets for a ListModule. It adds a Saved views section to the list settings menu where the current user can save, apply, and delete named presets.
Saved table views are different from ListLens. ListLens is server-defined PHP configuration. Saved table views are user-created presets stored by a provider.
Adminix stores only normalized table state:
current list search key, configured quick filter, configured filters, configured lens, and one valid configured sort key;
browser-only column visibility state for
hideable()fields whencolumnVisibility()is enabled;browser-side column order, width, and pin preferences for rendered list fields;
compact density when
densitySwitcher()is enabled.
Pagination is not saved. Applying a saved table view removes the current list page and applies the saved search/quick/filter/lens/sort state to the URL. Unrelated query parameters and other list modules are preserved.
Standard database storage
The built-in database provider is opt-in. Enable it in config/adminix.php and run migrations:
This creates adminix_table_views. Rows are scoped by server-derived owner key, page name, and module name. When a real admin user is authenticated, owner type and ID are derived from the current request user. When no_auth_access is enabled for local/demo use, table views share the anonymous owner scope.
Custom provider
For existing preference tables, multi-tenant storage, encrypted state, or non-SQL storage, bind or pass a custom provider:
Or configure a single module:
Providers implement:
adminix:doctor warns when savedTableViews() is enabled without standard storage, a module provider, or a custom TableViewProviderInterface binding.
Security notes:
browser requests cannot choose owner type, owner ID, page params, provider, datasource, fields, filters, lenses, or sortable fields;
Adminix verifies a signed table view context before every list/save/delete request;
server normalization drops unknown query keys, unknown columns, invalid sort directions, invalid column order/width/pin state, and unsupported UI state before the provider sees the request.
Reset controls
resetControls() adds a Reset view action to the list settings menu for the current ListModule. It is disabled until the current URL or browser storage contains state for that list.
Result: when the URL contains keys such as search-orders, quick-orders, filter-orders-status, sort-orders-created_at, lens-orders, or page, Adminix shows an enabled Reset view action inside the gear-icon list settings menu. The same action is enabled when browser-only view state exists for the list, such as hidden/shown columns, custom column order, custom column widths, custom pinned columns, or compact density. Clicking it returns to the same page without current-list URL state and clears current-list browser view state. Unrelated query parameters and another list module's view state are preserved.
This is a presentation and URL-state convenience only. It does not create a new server-side action endpoint and does not change list filters, row actions, datasource, or authorization behavior.
Mobile behavior
ListModule keeps the same PHP configuration and request contract on mobile. Search, lenses, filters, sorting, pagination, editable rows, row actions, and modal togglers still use the same module name and server-side configuration.
Example configuration:
Result:
desktop renders filters inline above the table;
narrow screens show a
Filtersbutton with an active-filter count;the button opens a drawer containing the same
ListFilter,ListDateFilter, andListDateRangeFiltercontrols;changing a filter still writes the same
filter-{moduleName}-{field}query key and resets pagination;wide tables scroll horizontally inside the table area, not across the whole Adminix shell;
row actions remain the existing buttons/forms/modal togglers, including route params, CSRF methods, confirm text, icons, and tooltips.
The mobile drawer is a presentation wrapper only. Do not add browser-owned datasource, action, or filter metadata; continue to configure lists from PHP classes.
Soft-delete row actions
restoreAction() and forceDeleteAction() enable built-in row forms for Eloquent models that use Laravel SoftDeletes. Use softDeleteActions() to enable both actions on the same list.
Result: Adminix renders Restore and Force delete controls only for rows whose source Eloquent model is currently trashed. Active rows do not receive those built-in controls. The forms submit with CSRF and a signed page/module/route-param context to the Adminix soft-delete action endpoint.
Requirements:
the list
dataSource()must be an Eloquent model class usingSoftDeletes;the list must expose trashed rows through
withDeleted()or through at least oneListLens::withDeleted();the primary key must be present in the rendered row, usually as a hidden
ListField;tenant/user boundaries must still be expressed in server-side
criteria(), policies, guards, or relation context.
On submit Adminix re-resolves the page, module, relation context, route params, datasource, primary key, criteria, active lens, filters, search, sorting, and current request scope from server-side configuration. It then verifies that the target record is still inside that scope and still trashed() before calling restore() or forceDelete(). Browser-provided datasource, writable fields, primary-key names, deleted state, tenant ids, and param:* values are not trusted.
forceDeleteAction() is irreversible. Use it only for records where permanent deletion is acceptable for your application.
adminix:doctor reports:
an error when restore/force-delete actions are configured on a non-Eloquent datasource or a model without
SoftDeletes;a warning when actions are enabled but neither the module nor any lens can expose soft-deleted rows.
Successful restore and force-delete operations dispatch audit events and flash typed Adminix notifications.
Clone actions
cloneAction() enables a built-in per-row clone form. bulkCloneAction($batchLimit = null) adds a built-in adminix_clone bulk action to the existing bulk action pipeline. Use cloneActions() to enable both on the same list.
Result: Adminix renders a Clone row action for rows with the configured primary key and a Clone option in the bulk action dropdown. The row form submits with CSRF and a signed page/module/route-param context. The bulk option uses the normal bulk endpoint, selected IDs, confirmation, batch limit, and scope filtering.
Clone data is derived server-side. By default Adminix copies rendered index ListField columns that exist in the datasource table. It skips the primary key, hidden fields, created_at, updated_at, and deleted_at. It also merges equality values from server-side criteria() after signed page params are resolved, so tenant or parent scope fields such as tenant_id = param:0 can be preserved even when the scope field is hidden.
On submit Adminix re-resolves the page, module, relation context, route params, datasource, primary key, criteria, active lens, filters, search, sorting, and current request scope from server-side configuration. The source record must still be inside the current list scope. The browser cannot choose the datasource, clone fields, writable fields, primary-key names, tenant ids, or param:* values.
Requirements and caveats:
the list
dataSource()must resolve to an Eloquent model table or a plain SQL table;at least one rendered index
ListFieldmust be backed by a datasource column after skipped fields are removed;unique columns, required hidden columns, custom defaults, file paths, and sensitive fields should be modeled intentionally in the consuming app before clone is enabled;
bulkCloneAction($batchLimit)can lower the default selected-row cap; the default is the normal bulk limit of1000;relation child lists keep their signed relation context and parent scope.
adminix:doctor reports:
an error when clone actions are configured for a datasource that cannot be resolved to an existing SQL/Eloquent table;
a warning when clone actions are enabled but no cloneable rendered index field remains.
Successful row and bulk clone operations dispatch resource.cloned audit events and flash typed Adminix notifications. Bulk clone also records the normal bulk.executed audit event with the scope-filtered selected IDs.
Actions
"Actions" is an array of control buttons for every single record, to do some actions, such as "open", "edit", "delete", or wherever else you need.
Example:
To add Action buttons to List module you need paste AlexKudrya\Adminix\Modules\Link\AdminixLinkModule or AlexKudrya\Adminix\Modules\Link\LinkModule class instances as an argument to addAction or addActions method of AdminixPage object.
Action buttons syntax is similar to Link module
By default, Adminix renders row actions inline in the final service column. For dense tables with many row controls, call rowActionsDropdown() on the same ListModule to render one compact vertical three-dots button per row. Clicking it opens a small contextual dropdown next to the trigger and shows the same configured actions inside the menu.
Result: every row keeps a single bi-three-dots-vertical trigger in the action column. The dropdown contains the existing GET links, POST/PUT/PATCH/DELETE forms with CSRF and confirm prompts, and modal togglers. If an action has an icon but no title, the dropdown uses the action name() as a text label so the menu remains readable. Rows where criteria() hides all actions still render an empty service cell to keep the table columns aligned.
rowActionsDropdown() is presentation-only. It does not change action authorization, criteria evaluation, route parameter substitution, signed modal relation context, request payloads, datasource, or persistence behavior.
Method | Description | |
|---|---|---|
name | Title which will be displayed on the button
name('EDIT')
| Optional |
uri | In case
uri(UserPage::URI)
In case
uri('edit_user')
| Required |
icon | Icon class name from Bootsrap icons Example: to generate in the Link an icon
icon('bi bi-people-fill')
| Optional |
method | Type of HTTP request, can be provided only by In case In case By default, it is equal to
method(HttpMethodsEnum::POST)
| Required if not GET |
params | Technically it is optional directive, but It is list of parameters which you added to your action (usually it is "id"). When Parameters adds to your link like this: / Example:
params(['record:id'])
For record with id = 123 action link will be | Required |
criteria | Determines display or not this action button for current record. For example if you need display action button only for users without admin status, you can do it like this:
criteria([
['is_admin', '==', false]
])
or
criteria([
['deleted_at', '!=', null]
])
Action visibility criteria are evaluated against the rendered list row, not the database query builder. For row actions this directive intentionally supports only | Optional |
New item link
Usually, in the admin panels, lists have the ability to add new records, and the New resource module is used for this. In order to display a link to a page with such a module in the correct place in the "list" module, there is this directive
Format is similar to Link module or Actions
Example:
or
Appearance examples
Here is example of List without editable mode:

Here is example of List with editable mode:
