K

Table

Sub-Rows

Sub-rows render related child records in an expandable panel below each parent row. Use them when users need to drill into detail — an invoice's line items, an order's shipments, a project's tasks — without leaving the table.

Sub-Rows preview

Sub-rows render related child records in an expandable panel below each parent row. Use them when users need to drill into detail — an invoice's line items, an order's shipments, a project's tasks — without leaving the table.

┌───┬────────────┬──────────────┬───────────┬──────────────┐
│ ▾ │ INV-1001 │ Northwind │ paid │ 9 350 Kč │ ← parent row
│ └──────────────────────────────────────────────────────┐
│ Product Qty Unit Line total Actions│ ← child table
│ 27" monitor 1 5 600 Kč 5 600 Kč [✎][🗑] │
│ Keyboard 2 1 200 Kč 2 400 Kč [✎][🗑] │
│ Wireless mouse 3 450 Kč 1 350 Kč [✎][🗑] │
│ Subtotal: 9 350 Kč │ ← per-parent total
│ └──────────────────────────────────────────────────────┘
│ ▸ │ INV-1002 │ Globex │ pending │ 18 100 Kč │
└───┴────────────┴──────────────┴───────────┴──────────────┘

Basic Setup

use NyonCode\WireTable\Columns\TextColumn;
use NyonCode\WireTable\Table;
 
public function table(Table $table): Table
{
return $table
->model(Invoice::class)
->columns([
TextColumn::make('number')->label('Invoice')->sortable(),
TextColumn::make('customer')->label('Customer'),
TextColumn::make('total')->money(),
])
->subRows('items') // Eloquent relationship method
->subRowColumns([ // columns for the child table
TextColumn::make('product')->label('Product'),
TextColumn::make('quantity')->numeric()->label('Qty'),
TextColumn::make('unit_price')->money()->label('Unit'),
]);
}

subRows('items') expects a relationship method (items()) on the parent model. The child columns are independent of the parent columns — they can be entirely different fields.

When to Use Them

Use sub-rows when:

  • a parent record owns a small set of child records,
  • users need quick drill-down without route changes,
  • the child data shares the same decision context as the parent row.

Avoid them when the child set is large enough to deserve its own table with its own filters and pagination — sub-rows are a detail affordance, not a second grid.

Expand and Collapse

->subRowsExpandable() // user can toggle (default true)
->subRowsDefaultExpanded() // start expanded
->subRowsExpandable(false) // always open, no toggle
->subRowsToggleLabel('Show items') // label for the toggle column

Expanded rows are tracked in Livewire state, so a user opens only the records they care about and the state survives re-renders. The toolbar also exposes Expand all / Collapse all controls.

Sortable Child Rows

Let users sort the child table by clicking its column headers, with an optional default sort applied before any interaction:

->subRowsSortable(default: 'line_total', direction: 'desc')
Product ↕ Qty ↕ Unit ↕ Line total ▼ ← ▼ active sort, ↕ sortable
─────────────────────────────────────────
27" monitor 1 5 600 Kč 5 600 Kč
Keyboard 2 1 200 Kč 2 400 Kč
Wireless ... 3 450 Kč 1 350 Kč

Only columns present in subRowColumns() may be sorted — arbitrary column names are rejected, so the sort is safe to drive from the request. Clicking the active column flips the direction; clicking another sorts it ascending. The active sort is shared across all expanded parents.

Limit and "Show More"

->subRowsLimit(5)

When a limit is set and more children exist, a "Show N more" button renders at the bottom of the child table. Clicking it reveals the full set for that parent (tracked per-parent in state), while the count stays accurate:

Product Qty Line total
───────────────────────────────
Keyboard 2 2 400 Kč
Wireless mouse 3 1 350 Kč
Show 1 more ← appears because limit (2) < total (3)

Filter the Child Query

Shape the underlying relationship query with subRowQuery():

use Illuminate\Database\Eloquent\Builder;
 
->subRowQuery(fn (Builder $query) => $query
->where('active', true)
->orderBy('sort_order')
)

Enable per-child interactive filters with subRowsFilterable(). A filter bar renders above the child table for any sortable/filterable sub-row column:

->subRowsFilterable()
Filter: [ Product… ] [ price from – to ] ✕ Reset
───────────────────────────────────────────────────────
Product Qty Line total
Keyboard 2 2 400 Kč ← only rows matching the filter

Row Actions

Render per-child actions in a trailing actions cell. Each action renders against the child record, exactly like main-table actions render against a parent:

use NyonCode\WireCore\Actions\Action;
use NyonCode\WireCore\Actions\DeleteAction;
 
->subRowActions([
Action::make('edit')->label('Edit')->icon('pencil')->color('primary'),
DeleteAction::make(),
])
Product Qty Line total Actions
─────────────────────────────────────────────
27" monitor 1 5 600 Kč [✎ Edit][🗑 Delete]
Keyboard 2 2 400 Kč [✎ Edit][🗑 Delete]

Per-Parent Subtotals

Give a sub-row column a subRows-scoped summary and the child table grows a footer with that aggregate for the parent's children:

->subRowColumns([
TextColumn::make('product')->label('Product'),
TextColumn::make('quantity')->numeric()->summarizeSum(scope: 'subRows'),
TextColumn::make('line_total')
->numeric(0)
->suffix(' Kč')
->summaryDecimals(0)
->summarizeSum('Subtotal', scope: 'subRows'),
])
Product Qty Line total
───────────────────────────────
27" monitor 1 5 600 Kč
Keyboard 2 2 400 Kč
Wireless mouse 3 1 350 Kč
Subtotal: 6 9 350 Kč ← per-parent footer

All aggregate types and number formatting from the Summaries page apply here. To total across all parents in the main footer, give a parent rollup column its own summary — see Grand totals across all children.

Flatten Mode

Flatten mode opens every parent's sub-rows at once, instead of letting the user expand them one at a time — handy for review and scanning where you want all detail visible together:

->flattenSubRows()
┌───┬────────────┬──────────────┐
│ ▾ │ INV-1001 │ 9 350 Kč │ every invoice is expanded,
│ └── Monitor … Keyboard … ───┘ not just the one the user clicked
│ ▾ │ INV-1002 │ 18 100 Kč │
│ └── Desk … Chair … ─────────┘
│ ▾ │ INV-1003 │ 8 450 Kč │
│ └── License … Support … ────┘
└───┴────────────┴──────────────┘

The runtime Expand all / Collapse all toolbar buttons toggle the same state, so users can switch between flattened and per-row drill-down on demand.

Detail-Row Mode (No Relation)

Skip subRows() entirely and provide only a child view: the expanded panel then renders the parent record itself — ideal for a detail card. Sub-rows activate as soon as subRowView() (or subRowColumns()) is set, even without a relation:

->subRowView('components.users.detail') // no subRows() call → detail-row mode
┌───┬────────────┬──────────┐
│ ▾ │ Alice │ Active │
│ └──────────────────────────────────┐
│ Email: alice@example.com │ ← your custom Blade
│ Phone: +420 777 123 456 │
│ └──────────────────────────────────┘
└───┴────────────┴──────────┘

Custom View

Replace the default child renderer entirely when the data is not naturally tabular:

->subRowView('components.orders.sub-rows')

The view receives $table, $component, $record (parent), $subRows (children collection), and layout variables.

Performance: Eager Loading

Sub-rows are loaded for the whole page in a single query rather than one query per expanded parent:

  • Flatten mode — every parent's children load at once.
  • Normal mode — only the currently expanded parents are loaded.

This removes the N+1 that would otherwise grow with the number of open rows. Reading a parent's children (and its subtotal count) then costs no extra queries. Eager loading is automatically skipped while interactive sub-row filters are active, since per-parent filtering falls back to a safe per-parent query.

Option Reference

Method Purpose
subRows(string $relation) Enable sub-rows from a relationship (omit it + set subRowView/subRowColumns for detail-row mode)
subRowColumns(array $columns) Columns for the child table
subRowQuery(Closure $cb) Shape the child relationship query
subRowsSortable(bool, ?string $default, string $direction) Click-to-sort headers + default sort
subRowActions(array $actions) Per-child row actions
subRowsLimit(?int) Cap children, enabling "Show N more"
subRowsFilterable(bool) Per-child interactive filter bar
subRowsExpandable(bool) Allow expand/collapse toggle
subRowsDefaultExpanded(bool) Start expanded
subRowsToggleLabel(?string) Label for the toggle column
flattenSubRows(bool) Render children as flat rows
subRowView(string) Custom child renderer