Skip to content

Create a List View

Lists display data in a table format with search, pagination, and actions.

Noerd Example App

File Locations

YAML Configuration:

bash
app-configs/{app}/lists/{name}-list.yml

Livewire Component:

bash
app-modules/{module}/resources/views/components/{name}-list.blade.php

List YAML files always live DIRECTLY in lists/ — never in subfolders. A nested Livewire component name (dots from a blade subfolder, e.g. booking::bookings.types-list) still resolves its config by the flat file name (lists/types-list.yml); the subfolder segments are ignored for lists. Layout overrides key off the same flat name. (Detail YAMLs keep their dot-to-subfolder mapping.)

The config is resolved against the app folders the tenant may use — current app first, then the other granted apps, then their module sources. A namespaced component (communication:: communications-list) additionally falls back to its OWN module's app folder (app-configs/communication/), so a module reached from another app — linked from its navigation, embedded in its detail, or opened as a modal from one of its tabs — still finds its title, columns and fields even when the tenant was never granted that module as an app. The fallback is searched last, so a granted app's own copy always wins. The same applies to detail and page YAMLs.

Example YAML Configuration

Example: app-configs/accounting/lists/customers-list.yml

yaml
title: Customers
actions:
  - label: New Customer
    action: listAction
disableSearch: false
columns:
  - field: name
    label: Name
    width: 12
    type: text
  - field: company_name
    label: Company Name
    width: 10
  - field: email
    label: Email
    width: 12
  - field: address
    label: Address
    width: 12
  - field: zipcode
    label: Zip Code
    width: 10
  - field: city
    label: City
    width: 10

List Properties

PropertyDescription
titlePage title (translation key)
descriptionOptional description text under the title
actionsArray of action buttons (see Actions below)
disableSearchDisable the search functionality
showSummaryShow or hide the summary row in the table footer (default: true)
showLineNumbersPrepend an Excel-style row-number column (restarts on every page)
multiSelectEnable the checkbox column and bulk-action bar on this list page (see Multi-Select & Bulk Actions below)
bulkActionsArray of bulk-action buttons shown when one or more rows are selected (see Multi-Select & Bulk Actions below)
searchableColumnsRestrict the search to specific fields (see List Search)
notSortableColumnsFields whose header must not be sortable (see List Search)
displayModetable (default) or grid — render the rows as a card grid instead of a table (see Grid Mode below)
gridColumnsCards per row in grid mode at the largest breakpoint, 16 (default: 4)
columnsArray of column definitions

Column Properties

PropertyDescriptionDefault
fieldModel attribute name
labelColumn header (translation key)
widthRelative column weight — widths are normalised across all columns (a column with width: 2 is twice as wide as one with width: 1)1
minWidthMinimum width in pixels (min-width)none
alignText alignment (left, right; number/currency auto-align right)left
typeDisplay type (see Column Types below)text
optionsvalue/label pairs for the badge type (see below)
readOnlyFor editable cell types (bool, inline inputs): render read-onlytrue
translatableMarks the column as language-dependent — the cell gets a subtle blue background so an editor sees that the value belongs to the selected language (see Languages)false
actionLivewire method called on cell click (receives the row id)openListRow
actionsArray of row actions rendered as a dropdown in this column — entries support label, route, modalComponent, action, confirm, heroicon
wireClick / wireClickFieldFor colored_text: custom wire:click method plus the row field passed as its argument/ id

Width behavior: The width value is applied as style="width: 15%;" on the <th> element. If the sum of all column widths exceeds 100, the table becomes wider than its container and horizontal scrolling is enabled.

Column Types

TypeDescription
textDefault. Standard text display
dateFormats value as date (YYYY-MM-DD)
datetimeFormats value as date + time (d.m.Y H:i in German locale, Y-m-d H:i otherwise)
numberRight-aligned number, rounded to 2 decimals
currencyRight-aligned number formatted as currency with
idClickable ID link
boolToggleable boolean: green checkmark (true), red circle (false). Clickable to toggle value
inverseboolGreen checkmark when true, nothing when false. Clickable to toggle value
badgeNeutral badge; the raw value is translated to a label via the column's options (value/label pairs). Columns mirroring a paired detail type: select field get this automatically
badge_with_textBadge with optional text (value must be array with badge and text keys)
relationBadgeBadge showing the display title of a foreign-key value (resolved via the registered relation types)
customAttributeValue from the custom_attributes JSON column, normalized for display (translatable arrays are resolved to the active language)
colored_textText with optional color classes; the row value may be an array with text, class, prefix, prefixClass, icon keys
relation_linkClickable link that opens a modal (requires idField plus either route or modalComponent in the column config; in route mode idParam defaults to modelId)

Automatic typing: Columns without an explicit type are typed from the database schema (booleanbool, numeric → number, date/datetime → matching type), and columns whose field mirrors a type: select field in the paired detail YAML ({x}-list{x}-detail) render as translated badges automatically. An explicit type or own options always wins.

Example:

yaml
columns:
  - field: name
    label: Name
    width: 30
    type: text
  - field: start_date
    label: Start Date
    width: 15
    type: date
  - field: is_active
    label: Active
    width: 10
    type: bool
  - field: is_emergency
    label: Emergency
    width: 10
    type: inversebool

Livewire Component

A list component declares its model as public $listModel and its detail target as public ?string $detailRoute (preferred) and/or public $detailComponent — everything else (query, modal opening, mounting, request handling) comes from the NoerdList trait.

Example: customers-list.blade.php

php
<?php

use Livewire\Component;
use Noerd\Traits\NoerdList;
use Noerd\Customer\Models\Customer;

new class extends Component {
    use NoerdList;

    public $listModel = Customer::class;
    public ?string $detailRoute = 'customer.detail';
    public $detailComponent = 'customer::customer-detail';
};
?>

<x-noerd::page>
    <x-noerd::list/>
</x-noerd::page>

The trait defaults build the query via listQuery($this->listModel) (which applies search, sort and the Excel-style column filters from the YAML config) and open the detail as a modal on row click.

$detailRoute wins when the named route is registered: the record opens as a modal AND the browser URL is rewritten to /{app}/{entity}/{id}?modal=true, so the link is shareable and a reload reopens the record over the previously visited page. $detailComponent stays as the fallback — keep both, so a list may reference a detail route owned by an optional module. See Modal System.

Custom Query Logic

When the list needs its own query (eager loads, extra wheres, row transformations), override listData() — keep $listModel declared and still build the query via listQuery($this->listModel). Example: products-list.blade.php

php
new class extends Component {
    use NoerdList;

    public $listModel = Product::class;
    public $detailComponent = 'product::product-page';

    public function listData(): array
    {
        $rows = $this->listQuery($this->listModel)
            ->whereDoesntHave('products', fn($query) => $query->withoutGlobalScopes())
            ->with('groups')
            ->paginate($this->perPage);

        foreach ($rows as $row) {
            $row->price_preview = $row->getPriceWording() . number_format($row->getGrossPrice(), 2, ',', '.');
        }

        return $this->buildList($rows);
    }
}; ?>

Never leave a custom query in with() once $listModel is declared: the generic trait features (row click, select-all, bulk delete) resolve the list via listData() and would operate on the wrong rows. Extra view data (e.g. a footer summary) stays in a slim with() that returns only the extra keys — share the query between listData() and with() via a private helper method:

php
private function filteredQuery(): Builder
{
    return $this->listQuery($this->listModel)
        ->when($this->customerId, fn ($query) => $query->where('customer_id', $this->customerId));
}

public function listData(): array
{
    return $this->buildList($this->filteredQuery()->paginate($this->perPage));
}

public function with(): array
{
    return [
        'summary' => [
            'name' => __('Total'),
            'total_gross' => $this->filteredQuery()->toBase()->sum('total_gross'),
        ],
    ];
}

Key Concepts

  • Trait: NoerdList provides all necessary properties and methods
  • $listModel: The Eloquent model backing the list — required for the trait defaults and the header actions (layout/object manager)
  • $detailRoute: Named detail route opened by listAction() — rewrites the browser URL to the record (preferred)
  • $detailComponent: The detail component opened by listAction() when no $detailRoute is registered
  • listData(): Builds the list config; override it for custom queries, always ending in return $this->buildList($rows);
  • listAction(): Trait default opens $detailRoute (else $detailComponent) as a modal with ['modelId' => $modelId]; only override it for custom behavior (extra modal arguments, no modal, …)
  • buildList(): Generates the list configuration from the YAML
  • request()->customerId / request()->create: Handled by the trait's rendering(); override rendering() when the list uses its own URL parameter (e.g. invoiceId)
  • <x-noerd::list />: Renders the table
  • Object permissions: Read/write/delete denial via the optional noerd.object-* gates (see AccessHelper in extension-registries.md) hides rows, header actions and the delete bulk action. The permission target is the model resolved by listQuery() / the declared $listModel. A repository-backed list without $listModel declares it explicitly — public ?string $objectPermissionModel = Order::class; — otherwise it stays unrestricted (reference: the liefertool orders list)

Default Sorting

Default sorting is configured in the list YAML — never in the component:

yaml
title: Invoices
defaultSort:
  field: invoice_date
  direction: desc   # optional, desc when omitted

Keys:

  • field: Column name to sort by
  • direction: asc (A-Z) or desc (Z-A); omitted means desc

mountList() applies the YAML default whenever the user has not sorted the list yet; a sort the user picks in the header is persisted per list in the session and always wins over the YAML default. Alternate list views (--{key}.yml) are complete standalone configs, so each view may bring its own defaultSort.

Without a defaultSort key, lists sort by id descending. There is no component API for default sorting: never set $sortField / $sortAsc directly and never override mount() for sorting.

See List Search for more details on search and sorting.

Actions

List components support multiple action buttons via the actions array in the YAML configuration.

YAML Configuration:

yaml
actions:
  - label: accounting_label_import
    action: openImportModal
    heroicon: arrow-up-tray
  - label: accounting_label_new_transaction
    action: listAction
PropertyDescription
labelTranslation key for the button text
route(optional) Named route opened as a modal — use this instead of action: listAction for the "New …" button
arguments(optional, with route) Arguments passed to the modal
actionLivewire method name to call (used when no route is given)
heroicon(optional) Heroicon name for the button icon
style(optional) Set to secondary for secondary button style. Default is primary

Button layout:

  • All buttons are primary style by default
  • Set style: secondary on individual actions for secondary style
  • Keyboard shortcut (N) applies only to the first button
  • Buttons are displayed side by side
  • No actions key means no button is rendered

Where the controls render (generic injection): the header controls — search, CSV export, registry list actions and the YAML action buttons — are injected by x-noerd::modal-title (noerd::components.table.list-controls) for EVERY component using the NoerdList trait. This covers the standard list header (list-header contributes only title, count, view switcher and filter chips) AND components with their own custom <x-slot:header> (e.g. a list nested in tab panels like the object manager): wrap the custom title in <x-noerd::modal-title> and the controls appear top right automatically — never hand-roll a search field or action buttons in a list header. Two props on x-noerd::modal-title tune the injection:

PropDescription
:listControls="false"Suppresses the injection (for headers without a real list behind them)
listControlsShowAlpine expression gating the controls' visibility, e.g. currentTab === 2

Standard single action (most common):

yaml
title: Customers
actions:
  - label: New Customer
    route: customer.detail

route: opens the detail route as a modal and writes /customer/new?modal=true into the address bar. action: listAction is the component-based equivalent and stays valid for lists whose detail has no route.

Multiple actions with icon:

yaml
title: accounting_label_bank_transactions
actions:
  - label: accounting_label_import
    action: openImportModal
    heroicon: arrow-up-tray
  - label: accounting_label_new_transaction
    action: listAction

PHP method for custom actions:

php
public function openImportModal(mixed $modelId = null, array $relations = []): void
{
    Noerd::modal('bank-transaction-import');
}

Requires the facade import: use Noerd\Facades\Noerd;

Custom methods must accept (mixed $modelId = null, array $relations = []) parameters to match the expected signature.

Multi-Select & Bulk Actions

Lists support a generic multi-select mode: a leading checkbox column plus a footer bar that acts on the ticked rows. The selected ids are tracked in the generic public array $selectedRecordIds property on the NoerdList trait — never re-implement this per list. There are two flavours.

1. Bulk-action page

Set multiSelect: true in the list YAML to show checkboxes on the list page. Row clicks still open the detail (only the checkbox ticks a row). When one or more rows are selected, a footer bar renders the buttons from the YAML bulkActions array.

yaml
title: Tasks
multiSelect: true
bulkActions:
  - label: Assign to
    action: assignSelected            # list-specific method on the component
    heroicon: user-plus
    style: secondary
  - label: Delete
    action: deleteSelected            # generic NoerdList method — works for any list
    heroicon: trash
    style: danger
    confirm: Delete the selected entries?   # optional: shown via wire:confirm
columns:
  - field: title
    label: Title

bulkActions properties:

PropertyDescription
labelButton text (translation key)
actionLivewire method called on the list component (required)
heroicon(optional) Heroicon name for the button icon
style(optional) secondary or danger. Default is primary
confirm(optional) Confirmation prompt (translation key) shown via wire:confirm

Generic vs. list-specific actions:

  • deleteSelected() lives in NoerdList — it deletes every selected id through the tenant-scoped query (firing model events, so observers/auditing still run). Wire it up purely from YAML; no per-list method is needed.

  • List-specific actions are public methods you add to the list component. They read the ticked ids from $this->selectedRecordIds. Example — open the task-create modal for the selected records:

    php
    use Noerd\Facades\Noerd;
    
    public function createTaskForSelected(): void
    {
        Noerd::modal('crm::task-create-modal', [
            'targetType' => 'Account',
            'selectedIds' => $this->selectedRecordIds,
        ]);
    }

After a bulk action that should clear the selection (e.g. opening a follow-up modal that finishes the job), reset it in a listener so the checkboxes clear:

php
use Livewire\Attributes\On;

#[On('tasksAssigned')]
public function onTasksAssigned(): void
{
    $this->selectedRecordIds = [];
}

2. Picker (return a selection to an opener)

Open any list as a modal with multiSelect and returnsSelection to use it as a record picker. In picker mode a row click ticks the row, the top "New …" action is hidden, and the footer shows Cancel / Apply selection instead of the bulk actions.

php
Noerd::modal('crm::accounts-list', [
    'multiSelect' => true,
    'returnsSelection' => true,
    'selectedRecordIds' => $this->selectedIds,   // pre-tick the current selection
    'context' => 'taskRecords',                  // disambiguates the result event
]);

On confirm the list dispatches recordsSelected with ids and context; the opener listens and filters by its context:

php
#[On('recordsSelected')]
public function recordsSelected(array $ids, mixed $context = null): void
{
    if ($context !== 'taskRecords') {
        return;
    }

    $this->selectedIds = array_values(array_map('intval', $ids));
}

Generic API on NoerdList

MemberPurpose
bool $multiSelectEnable the checkbox column (prop, or multiSelect: true in YAML)
bool $returnsSelectionPicker mode — row click ticks, footer is Cancel / Apply selection
array $selectedRecordIdsThe ticked ids
toggleRecordSelection($id)Toggle one row (wired to the row checkbox and, in picker mode, the row click)
toggleSelectAllVisible()Toggle every row on the current page (the header checkbox)
confirmRecordSelection()Dispatch recordsSelected + closeTopModal (picker footer)
deleteSelected()Generic bulk delete of the selected records

Notes:

  • Multi-select is always off in compact/embedded lists — checkboxes only appear on full pages and in pickers, never in a list embedded inside a detail view.
  • The checkbox's checked state is part of its wire:key, so the DOM is recreated when the selection changes — this guarantees the checkboxes clear after a bulk action (a plain morph can leave a user-toggled checkbox visually checked).
  • The footer (picker confirm bar vs. bulk-action bar) is decided by returnsSelection: when set, the confirm bar wins; otherwise the YAML bulkActions render once at least one row is selected.
  • Reference: app-configs/crm/lists/tasks-list.yml (bulk Assign to + Delete), leads-list/accounts-list (createTaskForSelected) for the page flavour; task-create-modal.blade.php openRecordPicker() for the picker.

Compact Mode (Embedded Lists)

Use compact mode when embedding a list inside another component — for example a related list rendered below the form of a detail view. In compact mode the list renders only the table and hides:

  • the list header (title, search field and action buttons such as "New …")
  • the inline title-search and description
  • the pagination footer (the "Showing 1 to N of N results" row and the per-page select)

compact is a public property on the NoerdList trait, so it works exactly like disableModal — just add it as an attribute on the embedded Livewire component.

For detail views, don't wire this up by hand. Use the generic <x-noerd::detail-lists> component instead — it renders the heading, the breakout wrappers and the compact list from a lists array in the detail YAML. See Embedded Lists in Detail Views.

The low-level flag (used internally by <x-noerd::detail-lists>):

blade
{{-- mx-8 cancels the disableModal -2rem breakout so the list aligns with the surrounding form --}}
<div class="mx-8">
    <livewire:crm::opportunities-list
        wire:key="account-opportunities-{{ $modelId }}"
        disableModal
        compact
        :accountId="$modelId" />
</div>

Notes:

  • noerd::components.list reads the flag via $compact = $compact ?? ($this->compact ?? false);, so the behaviour is generic — never duplicate it per module.
  • Compact mode also removes pagination, so only the first perPage rows are shown. Use it for narrowly-scoped lists (e.g. records that belong to the current detail record).
  • A list embedded with disableModal breaks out by -2rem (intended for full-page routes); the wrappers re-pad it so it aligns cleanly inside a modal or detail view.
  • disableModal never needs to be passed to <x-noerd::page> in the component's own view — the page component reads the flag from the Livewire component automatically ($disableModal = $disableModal ?? (($__livewire ?? null)?->disableModal ?? false);). An explicit :disableModal="true" attribute still overrides the property.

Multiple List Views (View Switcher)

A list can ship multiple YAML views — alternate configurations of the same list (different columns, title, actions). When at least two views exist, the list title turns into a dropdown button (title + record count + chevron) that lets the user switch the active view, similar to Salesforce list views.

Naming convention — sibling files in the same lists/ folder, suffixed with --{key}:

bash
app-configs/customer/lists/
├── customers-list.yml          # the default view
├── customers-list--vip.yml     # view "VIP Customers"
└── customers-list--inactive.yml
  • The view key is the suffix after -- (e.g. vip); -- is therefore reserved as the view separator and must not appear in list names themselves.
  • Each view file is a complete standalone list config (title, columns, actions, …) — nothing is merged from the base file.
  • Views may be project-only: a customers-list--vip.yml in the project's app-configs/ without a module copy is fine. Within one app a project file shadows a module-source file with the same view key.

Cross-app enumeration — the dropdown lists the views of EVERY app allowed for the tenant, not just the session's current app. A list name that exists in several apps (e.g. customers-list in delivery and customer) yields one entry per app, each labelled with its source app rendered with reduced opacity — e.g. "Kunden (Delivery)":

  • Every entry shows its source app label (the TenantApp title; Setup for the setup folder).
  • Current-app entries use plain view keys (default, vip); other apps' entries use composite {app}::{key} keys (delivery::default, delivery::vip). :: is therefore reserved and cannot appear in view keys.
  • Selecting another app's view renders that app's YAML via explicit-app resolution (StaticConfigHelper::getListConfigForApp()); the session's selected app is NOT changed.
  • Ordering: current app first, then the other allowed apps; default leads each app group, remaining variants alphabetical.
  • The dropdown label is the view file's title (translated via __()).

Behaviour:

  • The switcher only renders when ≥2 entries exist (across all apps), and never in compact/embedded lists or pickers.
  • The selected view is remembered per list in the session (listView.{component}) — as the composite {app}::{key} when it belongs to another app. If the view's YAML is removed, the list silently falls back to the default view.
  • The active view is also reflected in the URL as ?view={key} (plain vip, composite {app}--vip-- instead of :: keeps %3A%3A encoding out of the URL — or default for the standard view), so a shared link opens the same view — the default view included. On page load the URL param takes precedence over the session-saved view (and is persisted to the session, in :: form); an unknown key falls back to the session/default. Single-view lists never carry the param; embedded compact lists and pickers never read or write it.
  • Because the whole config is swapped, the view's own searchableColumns, actions, notSortableColumns and column types all apply automatically. DB-driven layout overrides key per view file (e.g. customers-list--vip), app-agnostic — a restriction on vip also hides every other app's {app}::vip entry.

Generic API:

MemberPurpose
?string $listViewActive plain view key (null = base YAML) on the NoerdList trait
?string $listViewAppSource-app folder of the active view (null = current app)
?string $listViewParamURL-bound (#[Url(as: 'view')]) key of the active view incl. 'default'; composite keys use -- (gastro--vip); null = single-view/embedded list
switchListView(string $key)Switch and persist the active view ('default' = base YAML; accepts composite keys)
availableListViews (computed)['{viewKey}' => ['key' => …, 'app' => …, 'appLabel' => …, 'title' => …], …]
StaticConfigHelper::getListViews($component)The underlying cross-app discovery helper
StaticConfigHelper::getListConfigForApp($app, $name)Load a list config for an explicit app
StaticConfigHelper::parseListViewKey($key)[appFolder|null, plainKey] from a dropdown key
StaticConfigHelper::composeListViewKey($app, $key)Inverse of parseListViewKey()

CSV Export

A list can offer a CSV download of its (filtered) query. Enable it with the component property public bool $enableCsvExport = true; and override prepareCsvExport():

php
protected function prepareCsvExport(): array
{
    $config = $this->getListConfig();

    return [
        $this->listQuery($this->listModel),   // Builder — search/sort/filters already applied
        $config['columns'] ?? [],             // columns to export (list YAML shape)
        'customers.csv',                      // download filename
    ];
}
  • The export button appears in the list header next to the search field
  • The file streams with a UTF-8 BOM and ; as delimiter (Excel-friendly); headers are the translated column labels; rows are read lazily in chunks
  • formatCsvValue($value, $column) formats each cell by column type (bool → Yes/No, badge → the translated option label, dates/currency accordingly) — override it for custom formats
  • prepareExportRow($row) is an optional per-row hook (e.g. to eager-compute accessors)

Grid Mode (Card Layout)

Any list can render its rows as a card grid instead of the table — purely through the list YAML, with zero changes to the component's Blade file:

yaml
title: POS
description: Select a customer to start a new order.
displayMode: grid
gridColumns: 4
columns:
  - field: name
    label: Name
  - field: company_name
    label: Company
  - field: city
    label: City
searchableColumns:
  - name
  - company_name
  - city

Grid mode swaps only the rows block (noerd::components.list.grid) — the list header (title, search, view switcher, actions, filter chips), the pagination footer, the picker/bulk footers and the object-permission handling stay exactly as in table mode.

Card content is derived from the columns array: the first column with a non-empty value renders as the bold card title, every remaining column as a secondary line; empty values are skipped entirely (so a missing name falls through to the next column as the title). Column types are honored like in minimal mode: currency, date, datetime, bool are formatted, badge renders as a translated pill; everything else renders as text (data_get, so dotted fields work).

Cards per row: gridColumns (16, default 4) sets the count at the largest breakpoint; smaller viewports collapse responsively (1 column on mobile, 2 from sm, 3 from lg). The classes come from a static map — Tailwind cannot generate class names at runtime, so only these values are supported; an unknown value falls back to 4.

Row click behaves exactly like a table row click (openListRow$detailRoute / $detailComponent, or a custom listAction() override), including the keyboard navigation (arrow keys + Enter) and picker mode. In multiSelect mode each card gets a checkbox in its top-right corner wired to toggleRecordSelection.

Column filters and sorting are offered as a control bar above the cards (noerd::components.list.grid-controls), since grid mode renders neither the header funnels nor the sort headers:

  • Left: one labeled funnel button per filterable column, opening the very same popover as the header funnel of a table list (see List Filters).
  • Right: a sort dropdown listing every sortable column — the same rule as the table header (NoerdList::isSortableColumn(): not action, not dotted, not in notSortableColumns), so relation/JSON paths are absent there as well; the query cannot order by them. Picking a column calls sortBy() (re-picking the active column flips the direction); below a separator, two entries set the direction explicitly via setSortDirection(). The trigger names the active column (Sort by: Name) with an up/down icon, or reads just Sort by while the list sorts on a column that is not part of the YAML (the technical id default).

The bar sits above the empty state too, so a filter that matches nothing stays clearable; active filters additionally show as chips next to the list title. Compact/embedded grid lists render no control bar at all (they apply no column filters, and the table variant hides its header there too).

Not rendered in grid mode (thead-only features): the select-all checkbox, showLineNumbers and the summary footer.

A list mounted as a minimal widget ignores displayMode — minimal mode takes precedence.

Minimal Mode (List Widgets)

Besides compact mode, a list can render as a minimal widget: a slim, column-restricted variant used by the page widget sidebar (<x-noerd::detail-widget> mounts the embedded list this way). Mount properties:

PropertyDescription
minimalEnable the minimal variant
minimalColumnsField names to render, in order (subset of the YAML columns)
minimalLimitRow limit (default 5; replaces pagination)
showMoreRouteNamed list route for the "Show more" link — opened as a modal WITHOUT rewriting the URL (the modal shows the list narrowed by showMoreArguments)
showMoreComponentList component for "Show more" — fallback when the route is not registered
showMoreArgumentsArguments passed to the "Show more" list (usually the same narrowing filter)
blade
<livewire:crm::opportunities-list
    wire:key="widget-opportunities-{{ $modelId }}"
    :minimal="true"
    :minimalColumns="['name', 'amount']"
    :showMoreRoute="'crm.opportunities'"
    :showMoreArguments="['accountId' => $modelId]" />

Next Steps

Continue with Create a Detail View to build forms for editing records.