Elyra
Elyra The coding agent eTerm The terminal that knows where each command ends Huskarl AI colleagues on your own Mac Starf An activity monitor for Apple silicon that never invents a number etrans An SSH and SFTP client for macOS Elyra VM Virtual machines for macOS, Linux and Windows on your Mac Litr A small, native web browser for macOS Notr A notebook for macOS 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 Refr Local-first PDF workspace for macOS Elyra Workspace A desktop workspace for coding agents Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Elyra
Inertia integration (Svelte / Vue)

Inertia integration (Svelte / Vue)

The Svelte and Vue clients are client-driven: a headless engine calls a fetch function that POSTs the protocol request to a Laravel endpoint. This guide covers the endpoints, CSRF, and server-seeded first paint.

Endpoints

Define the grid once and reuse it across the page, grid, mutate and export routes:

use Elyra\DataGrid\Protocol\GridRequest;
use Elyra\DataGrid\Protocol\GridMutation;
use Elyra\DataGrid\Server\Grid;
use Illuminate\Http\Request;

function salesGrid()
{
    return Grid::table('sales')->connection('elyrasql')->key('id')
        ->columns(['id', 'sku', 'region', 'category', 'status'])
        ->column('status', editable: true, editor: 'select', rules: 'required|in:pending,paid,shipped')
        ->column('qty', type: 'number', editable: true, rules: 'required|integer|min:0')
        ->column('revenue', type: 'number')
        ->searchable(['sku', 'category']);
}

Route::get('/sales', fn () => Inertia::render('Sales', [
    'initial' => salesGrid()->respond(GridRequest::fromArray([
        'columns' => ['id', 'sku', 'region', 'category', 'status', 'qty', 'revenue'],
        'view' => ['skip' => 0, 'take' => 20],
        'meta' => ['total'],
    ])),
]));

Route::post('/api/sales/grid',   fn (Request $r) => salesGrid()->respond(GridRequest::fromArray($r->all())));
Route::post('/api/sales/mutate', fn (Request $r) => salesGrid()->mutate(GridMutation::fromArray($r->all())));
Route::post('/api/sales/export', fn (Request $r) => salesGrid()->export(
    GridRequest::fromArray(json_decode($r->input('request'), true) ?: []), 'csv', 'sales'));

CSRF

The laravelFetch helper posts JSON. The simplest approach for API-style grid endpoints is to exempt them from CSRF in bootstrap/app.php:

->withMiddleware(function (Middleware $middleware): void {
    $middleware->validateCsrfTokens(except: ['api/*']);
})

Alternatively keep CSRF and add a <meta name="csrf-token"> tag — laravelFetch sends it as X-CSRF-TOKEN.

Server-seeded first paint (initialData)

A client-fetched grid is empty on first paint until its XHR resolves. Pass the first page as an Inertia prop and hand it to the grid as initialData — the grid renders immediately and only fetches on interaction:

<script setup lang="ts">
import { DataGrid, laravelFetch } from '@elyra/datagrid-vue';
import type { GridResponse } from '@elyra/datagrid-vue';

const props = defineProps<{ initial?: GridResponse }>();

const options = {
  key: 'id',
  columns: [ /* ... */ ],
  fetch: laravelFetch('/api/sales/grid'),
  initialData: props.initial,   // ← instant first paint
};
</script>

This also improves perceived performance and works well with Inertia SSR.

The laravelFetch helper

import { laravelFetch } from '@elyra/datagrid-vue'; // or -svelte
const fetch = laravelFetch('/api/sales/grid');

It POSTs the GridRequest as JSON with Accept: application/json, X-CSRF-TOKEN (from the meta tag, if present) and X-Requested-With. You can provide any (request) => Promise<GridResponse> if you need custom behavior.

Custom mutate

editing.mutate is any (GridMutation) => Promise<GridMutationResult>:

editing: {
  mode: 'slideover',
  mutate: async (m) => (await fetch('/api/sales/mutate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
    body: JSON.stringify(m),
  })).json(),
},