Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra DataGrid Server-driven data grid for Laravel
Elyra
Master-detail & tree grid

Master-detail & tree grid

Two ways to show hierarchy: master-detail (an expandable panel under a row) and a tree grid (self-referencing parent/child data with lazy children).

Master-detail

Expand a row to reveal a detail panel — a custom view, or a nested grid.

Livewire

class OrdersGrid extends DataGrid
{
    public bool $masterDetail = true;
    public ?string $detailView = 'orders.detail';  // Blade view, rendered with ['row' => ...]
    // ...
}

The grid adds an expand column; expanding renders your detailView (or a key–value fallback if none is set). Because the detail view is arbitrary Blade, it can contain another grid.

Vue / Svelte (core)

The core exposes the mechanism; the detail payload is loaded lazily:

const options = {
  /* ... */
  masterDetail: { load: (row) => fetch(`/api/orders/${row.id}/lines`).then(r => r.json()) },
};

grid.toggleExpand(rowId);
grid.isExpanded(rowId);
grid.detailOf(rowId);   // the loaded payload

Tree grid

Bind self-referencing data (a parent_id column). Root rows load first; expanding a node lazily fetches its children (WHERE parent = id).

const options = {
  key: 'id',
  columns: [
    { field: 'name', header: 'Name' },     // first column shows the tree
    { field: 'kind', header: 'Kind' },
    { field: 'headcount', header: 'Headcount', type: 'number', align: 'right' },
  ],
  fetch: laravelFetch('/api/org/grid'),
  initialData: props.initial,             // server-seeded roots
  tree: {
    parentField: 'parent_id',
    rootValue: 0,                          // value marking roots (default null)
    hasChildrenField: 'has_children',      // boolean column → show a chevron
  },
};
  • The first column renders indentation + an expand chevron.
  • Children are fetched on demand and inserted after their parent with the next depth.
  • Roots can be server-seeded via initialData for an instant first paint.
await grid.toggleTreeNode(nodeId);
grid.isTreeExpanded(nodeId);

Server

No special server support is needed — children are just a filtered request:

function orgGrid()
{
    return Grid::table('org')->connection('elyrasql')->key('id')
        ->columns(['id', 'name', 'kind', 'headcount', 'has_children', 'parent_id'])
        ->column('budget', type: 'number');
}

// roots (server-seeded)
Route::get('/org', fn () => Inertia::render('Org', [
    'initial' => orgGrid()->respond(GridRequest::fromArray([
        'columns' => ['id', 'name', 'kind', 'headcount', 'has_children'],
        'filter' => ['logic' => 'and', 'filters' => [['field' => 'parent_id', 'op' => 'eq', 'value' => 0]]],
        'view' => ['skip' => 0, 'take' => 50],
    ])),
]));

// children (lazy)
Route::post('/api/org/grid', fn (Request $r) => orgGrid()->respond(GridRequest::fromArray($r->all())));

Declare parent_id (and has_children) as columns so the client can filter on them, even if you do not display them.