Chapter 10 of 34

One model, every framework

Svelte 4/5, Solid and Preact – the model stays the same, only the event syntax changes.

After this chapter you can

  • compare the event syntax of all wrappers
  • use Svelte 5 callback props
  • avoid two Solid runtimes

The same model everywhere

All framework demos import one function – createOrderTableModel() from chapter 6. The TableModelIf knows nothing about frameworks; only the event syntax differs.

FrameworkPackageEvents
Plain TS@guiexpert/tableEventAdapter / own EventListenerIf
Web Component@guiexpert/webcomponent-tableaddEventListener("tableReady", e => e.detail)
React / Preact / Solid@guiexpert/react-table / preact-table / solid-tableprops tableReady={api => …}
Vue 3@guiexpert/vue3-table@tableReady="onReady"
Svelte 4 / 5@guiexpert/svelte-tableon:tableReady resp. Svelte 5: onTableReady={api => …}
Angular 14–22@guiexpert/angular-table@NN(tableReady)="onReady($event)"

Svelte 5 (runes)

Terminal window
npm i @guiexpert/table @guiexpert/svelte-table
frameworks/svelte/src/App.svelte
<!-- Episode 010 – Svelte 5 (runes): callback props instead of on:… events. -->
<script lang="ts">
import { GuiexpertTable } from "@guiexpert/svelte-table";
import { type GeMouseEvent, type TableApi, TableOptions } from "@guiexpert/table";
import { createOrderTableModel } from "./model/order-table";
const tableModel = createOrderTableModel(100_000);
const tableOptions = { ...new TableOptions() };
let info = $state("");
</script>
<header class="app-header"><b>Svelte 5</b> <span id="info">{info}</span></header>
<div class="table-div">
<GuiexpertTable
{tableModel}
{tableOptions}
onTableReady={(api: TableApi) => (info = `${api.getBodyModel().getRowCount()} orders`)}
onMouseClicked={(evt: GeMouseEvent) => (info = `row ${evt.rowIndex}, column ${evt.columnIndex}`)}
/>
</div>

The package ships Svelte source, compiled by your Svelte – so it runs under Svelte 4 and 5. In Svelte 5 use the callback props (onTableReady, onMouseClicked …); in Svelte 4 the on:tableReady events with e.detail.

Svelte 5 with runes.
Svelte 5 with runes.

Solid

frameworks/solid/src/App.tsx
// Episode 010 – Solid: callback props like React.
import { GuiexpertTable } from "@guiexpert/solid-table";
import { GeMouseEvent, TableApi, TableOptions } from "@guiexpert/table";
import { createSignal } from "solid-js";
import { createOrderTableModel } from "./model/order-table";
export function App() {
const [info, setInfo] = createSignal("");
const tableModel = createOrderTableModel(100_000);
const tableOptions = { ...new TableOptions() };
return (
<>
<header class="app-header"><b>Solid</b> <span id="info">{info()}</span></header>
<div class="table-div">
<GuiexpertTable
tableModel={tableModel}
tableOptions={tableOptions}
tableReady={(api: TableApi) => setInfo(`${api.getBodyModel().getRowCount()} orders`)}
mouseClicked={(evt: GeMouseEvent) => setInfo(`row ${evt.rowIndex}, column ${evt.columnIndex}`)}
/>
</div>
</>
);
}
frameworks/solid/vite.config.ts
import { defineConfig } from "vite";
import solid from "vite-plugin-solid";
export default defineConfig({
plugins: [solid()],
resolve: { dedupe: ["solid-js"] }, // exactly one Solid runtime for app and @guiexpert/solid-table
server: { fs: { allow: ["../.."] } }
});

Preact

frameworks/preact/src/App.tsx
// Episode 010 – Preact: same callback props as React.
import { GuiexpertTable } from "@guiexpert/preact-table";
import { GeMouseEvent, TableApi, TableOptions } from "@guiexpert/table";
import { useMemo, useState } from "preact/hooks";
import { createOrderTableModel } from "./model/order-table";
export function App() {
const [info, setInfo] = useState("");
const tableModel = useMemo(() => createOrderTableModel(100_000), []);
const tableOptions = useMemo(() => ({ ...new TableOptions() }), []);
return (
<>
<header class="app-header"><b>Preact</b> <span id="info">{info}</span></header>
<div class="table-div">
<GuiexpertTable
tableModel={tableModel}
tableOptions={tableOptions}
tableReady={(api: TableApi) => setInfo(`${api.getBodyModel().getRowCount()} orders`)}
mouseClicked={(evt: GeMouseEvent) => setInfo(`row ${evt.rowIndex}, column ${evt.columnIndex}`)}
/>
</div>
</>
);
}

Takeaway

Write your table model and options once, in plain TypeScript, and test them without a framework. Switching from React to Vue – or using the same table in an Angular admin app and a Svelte dashboard – only changes a dozen lines of glue code. Part 4 is all about that model: chapter 11 (TableModel & areas).

API used in this chapter