From 4e8a35d4cbc778923d6835b73b0ebfb94023fad2 Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Wed, 24 Jun 2026 11:44:40 -0500 Subject: [PATCH 1/2] docs: rearrange docs sidebar some --- docs/config.json | 98 ++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/docs/config.json b/docs/config.json index 07ad5775f8..54654b80a6 100644 --- a/docs/config.json +++ b/docs/config.json @@ -92,49 +92,56 @@ "label": "angular", "children": [ { "label": "Table State", "to": "framework/angular/guide/table-state" }, - { "label": "Composable Tables", "to": "framework/angular/guide/composable-tables" } + { "label": "Composable Tables (createTableHook)", "to": "framework/angular/guide/composable-tables" }, + { "label": "Custom Plugins", "to": "framework/angular/guide/custom-features" } ] }, { "label": "lit", "children": [ { "label": "Table State", "to": "framework/lit/guide/table-state" }, - { "label": "Composable Tables", "to": "framework/lit/guide/composable-tables" } + { "label": "Composable Tables (createTableHook)", "to": "framework/lit/guide/composable-tables" }, + { "label": "Custom Plugins", "to": "framework/lit/guide/custom-features" } ] }, { "label": "react", "children": [ { "label": "Table State", "to": "framework/react/guide/table-state" }, - { "label": "Composable Tables", "to": "framework/react/guide/composable-tables" } + { "label": "Composable Tables (createTableHook)", "to": "framework/react/guide/composable-tables" }, + { "label": "Custom Plugins", "to": "framework/react/guide/custom-features" } ] }, { "label": "preact", "children": [ { "label": "Table State", "to": "framework/preact/guide/table-state" }, - { "label": "Composable Tables", "to": "framework/preact/guide/composable-tables" } + { "label": "Composable Tables (createTableHook)", "to": "framework/preact/guide/composable-tables" }, + { "label": "Custom Plugins", "to": "framework/preact/guide/custom-features" } ] }, { "label": "solid", "children": [ { "label": "Table State", "to": "framework/solid/guide/table-state" }, - { "label": "Composable Tables", "to": "framework/solid/guide/composable-tables" } + { "label": "Composable Tables (createTableHook)", "to": "framework/solid/guide/composable-tables" }, + { "label": "Custom Plugins", "to": "framework/solid/guide/custom-features" } ] }, { "label": "svelte", "children": [ { "label": "Table State", "to": "framework/svelte/guide/table-state" }, - { "label": "Composable Tables", "to": "framework/svelte/guide/composable-tables" } + { "label": "Composable Tables (createTableHook)", "to": "framework/svelte/guide/composable-tables" }, + { "label": "Custom Plugins", "to": "framework/svelte/guide/custom-features" } ] }, { "label": "vue", "children": [ { "label": "Table State", "to": "framework/vue/guide/table-state" }, - { "label": "Composable Tables", "to": "framework/vue/guide/composable-tables" } + { "label": "Composable Tables (createTableHook)", "to": "framework/vue/guide/composable-tables" }, + { "label": "Custom Plugins", "to": "framework/vue/guide/custom-features" } ] }, { @@ -167,8 +174,7 @@ { "label": "Row Pinning", "to": "framework/angular/guide/row-pinning" }, { "label": "Row Selection", "to": "framework/angular/guide/row-selection" }, { "label": "Sorting", "to": "framework/angular/guide/sorting" }, - { "label": "Virtualization", "to": "framework/angular/guide/virtualization" }, - { "label": "Custom Features", "to": "framework/angular/guide/custom-features" } + { "label": "Virtualization", "to": "framework/angular/guide/virtualization" } ] }, { @@ -189,8 +195,7 @@ { "label": "Row Pinning", "to": "framework/lit/guide/row-pinning" }, { "label": "Row Selection", "to": "framework/lit/guide/row-selection" }, { "label": "Sorting", "to": "framework/lit/guide/sorting" }, - { "label": "Virtualization", "to": "framework/lit/guide/virtualization" }, - { "label": "Custom Features", "to": "framework/lit/guide/custom-features" } + { "label": "Virtualization", "to": "framework/lit/guide/virtualization" } ] }, { @@ -211,8 +216,7 @@ { "label": "Row Pinning", "to": "framework/react/guide/row-pinning" }, { "label": "Row Selection", "to": "framework/react/guide/row-selection" }, { "label": "Sorting", "to": "framework/react/guide/sorting" }, - { "label": "Virtualization", "to": "framework/react/guide/virtualization" }, - { "label": "Custom Features", "to": "framework/react/guide/custom-features" } + { "label": "Virtualization", "to": "framework/react/guide/virtualization" } ] }, { @@ -233,8 +237,7 @@ { "label": "Row Pinning", "to": "framework/preact/guide/row-pinning" }, { "label": "Row Selection", "to": "framework/preact/guide/row-selection" }, { "label": "Sorting", "to": "framework/preact/guide/sorting" }, - { "label": "Virtualization", "to": "framework/preact/guide/virtualization" }, - { "label": "Custom Features", "to": "framework/preact/guide/custom-features" } + { "label": "Virtualization", "to": "framework/preact/guide/virtualization" } ] }, { @@ -255,8 +258,7 @@ { "label": "Row Pinning", "to": "framework/solid/guide/row-pinning" }, { "label": "Row Selection", "to": "framework/solid/guide/row-selection" }, { "label": "Sorting", "to": "framework/solid/guide/sorting" }, - { "label": "Virtualization", "to": "framework/solid/guide/virtualization" }, - { "label": "Custom Features", "to": "framework/solid/guide/custom-features" } + { "label": "Virtualization", "to": "framework/solid/guide/virtualization" } ] }, { @@ -277,8 +279,7 @@ { "label": "Row Pinning", "to": "framework/svelte/guide/row-pinning" }, { "label": "Row Selection", "to": "framework/svelte/guide/row-selection" }, { "label": "Sorting", "to": "framework/svelte/guide/sorting" }, - { "label": "Virtualization", "to": "framework/svelte/guide/virtualization" }, - { "label": "Custom Features", "to": "framework/svelte/guide/custom-features" } + { "label": "Virtualization", "to": "framework/svelte/guide/virtualization" } ] }, { @@ -299,8 +300,7 @@ { "label": "Row Pinning", "to": "framework/vue/guide/row-pinning" }, { "label": "Row Selection", "to": "framework/vue/guide/row-selection" }, { "label": "Sorting", "to": "framework/vue/guide/sorting" }, - { "label": "Virtualization", "to": "framework/vue/guide/virtualization" }, - { "label": "Custom Features", "to": "framework/vue/guide/custom-features" } + { "label": "Virtualization", "to": "framework/vue/guide/virtualization" } ] } ] @@ -868,6 +868,7 @@ { "label": "angular", "children": [ + { "label": "Kitchen Sink (All Features)", "to": "framework/angular/examples/kitchen-sink" }, { "label": "Column Filters", "to": "framework/angular/examples/filters" }, { "label": "Column Filters (Faceted)", "to": "framework/angular/examples/filters-faceted" }, { "label": "Fuzzy Search Filters", "to": "framework/angular/examples/filters-fuzzy" }, @@ -880,17 +881,18 @@ { "label": "Performant Column Resizing", "to": "framework/angular/examples/column-resizing-performant" }, { "label": "Column Visibility", "to": "framework/angular/examples/column-visibility" }, { "label": "Expanding", "to": "framework/angular/examples/expanding" }, + { "label": "Expanding Sub Components", "to": "framework/angular/examples/sub-components" }, { "label": "Grouping", "to": "framework/angular/examples/grouping" }, { "label": "Pagination", "to": "framework/angular/examples/pagination" }, { "label": "Row Pinning", "to": "framework/angular/examples/row-pinning" }, { "label": "Row Selection", "to": "framework/angular/examples/row-selection" }, - { "label": "Sorting", "to": "framework/angular/examples/sorting" }, - { "label": "Kitchen Sink (All Features)", "to": "framework/angular/examples/kitchen-sink" } + { "label": "Sorting", "to": "framework/angular/examples/sorting" } ] }, { "label": "lit", "children": [ + { "label": "Kitchen Sink (All Features)", "to": "framework/lit/examples/kitchen-sink" }, { "label": "Column Filters", "to": "framework/lit/examples/filters" }, { "label": "Column Filters (Faceted)", "to": "framework/lit/examples/filters-faceted" }, { "label": "Fuzzy Search Filters", "to": "framework/lit/examples/filters-fuzzy" }, @@ -903,18 +905,19 @@ { "label": "Performant Column Resizing", "to": "framework/lit/examples/column-resizing-performant" }, { "label": "Column Visibility", "to": "framework/lit/examples/column-visibility" }, { "label": "Expanding", "to": "framework/lit/examples/expanding" }, + { "label": "Expanding Sub Components", "to": "framework/lit/examples/sub-components" }, { "label": "Grouping", "to": "framework/lit/examples/grouping" }, { "label": "Pagination", "to": "framework/lit/examples/pagination" }, { "label": "Row Pinning", "to": "framework/lit/examples/row-pinning" }, { "label": "Row Selection", "to": "framework/lit/examples/row-selection" }, { "label": "Sorting", "to": "framework/lit/examples/sorting" }, - { "label": "Sorting (Dynamic Data)", "to": "framework/lit/examples/sorting-dynamic-data" }, - { "label": "Kitchen Sink (All Features)", "to": "framework/lit/examples/kitchen-sink" } + { "label": "Sorting (Dynamic Data)", "to": "framework/lit/examples/sorting-dynamic-data" } ] }, { "label": "react", "children": [ + { "label": "Kitchen Sink (All Features)", "to": "framework/react/examples/kitchen-sink" }, { "label": "Column Filters", "to": "framework/react/examples/filters" }, { "label": "Column Filters (Faceted)", "to": "framework/react/examples/filters-faceted" }, { "label": "Fuzzy Search Filters", "to": "framework/react/examples/filters-fuzzy" }, @@ -928,18 +931,19 @@ { "label": "Performant Column Resizing", "to": "framework/react/examples/column-resizing-performant" }, { "label": "Column Visibility", "to": "framework/react/examples/column-visibility" }, { "label": "Expanding", "to": "framework/react/examples/expanding" }, + { "label": "Expanding Sub Components", "to": "framework/react/examples/sub-components" }, { "label": "Grouping", "to": "framework/react/examples/grouping" }, { "label": "Pagination", "to": "framework/react/examples/pagination" }, { "label": "Row DnD", "to": "framework/react/examples/row-dnd" }, { "label": "Row Pinning", "to": "framework/react/examples/row-pinning" }, { "label": "Row Selection", "to": "framework/react/examples/row-selection" }, - { "label": "Sorting", "to": "framework/react/examples/sorting" }, - { "label": "Kitchen Sink (All Features)", "to": "framework/react/examples/kitchen-sink" } + { "label": "Sorting", "to": "framework/react/examples/sorting" } ] }, { "label": "solid", "children": [ + { "label": "Kitchen Sink (All Features)", "to": "framework/solid/examples/kitchen-sink" }, { "label": "Column Filters", "to": "framework/solid/examples/filters" }, { "label": "Column Filters (Faceted)", "to": "framework/solid/examples/filters-faceted" }, { "label": "Fuzzy Search Filters", "to": "framework/solid/examples/filters-fuzzy" }, @@ -952,17 +956,18 @@ { "label": "Performant Column Resizing", "to": "framework/solid/examples/column-resizing-performant" }, { "label": "Column Visibility", "to": "framework/solid/examples/column-visibility" }, { "label": "Expanding", "to": "framework/solid/examples/expanding" }, + { "label": "Expanding Sub Components", "to": "framework/solid/examples/sub-components" }, { "label": "Grouping", "to": "framework/solid/examples/grouping" }, { "label": "Pagination", "to": "framework/solid/examples/pagination" }, { "label": "Row Pinning", "to": "framework/solid/examples/row-pinning" }, { "label": "Row Selection", "to": "framework/solid/examples/row-selection" }, - { "label": "Sorting", "to": "framework/solid/examples/sorting" }, - { "label": "Kitchen Sink (All Features)", "to": "framework/solid/examples/kitchen-sink" } + { "label": "Sorting", "to": "framework/solid/examples/sorting" } ] }, { "label": "svelte", "children": [ + { "label": "Kitchen Sink (All Features)", "to": "framework/svelte/examples/kitchen-sink" }, { "label": "Column Filters", "to": "framework/svelte/examples/filtering" }, { "label": "Column Filters (Faceted)", "to": "framework/svelte/examples/filters-faceted" }, { "label": "Fuzzy Search Filters", "to": "framework/svelte/examples/filters-fuzzy" }, @@ -975,17 +980,18 @@ { "label": "Performant Column Resizing", "to": "framework/svelte/examples/column-resizing-performant" }, { "label": "Column Visibility", "to": "framework/svelte/examples/column-visibility" }, { "label": "Expanding", "to": "framework/svelte/examples/expanding" }, + { "label": "Expanding Sub Components", "to": "framework/svelte/examples/sub-components" }, { "label": "Grouping", "to": "framework/svelte/examples/grouping" }, { "label": "Pagination", "to": "framework/svelte/examples/pagination" }, { "label": "Row Pinning", "to": "framework/svelte/examples/row-pinning" }, { "label": "Row Selection", "to": "framework/svelte/examples/row-selection" }, - { "label": "Sorting", "to": "framework/svelte/examples/sorting" }, - { "label": "Kitchen Sink (All Features)", "to": "framework/svelte/examples/kitchen-sink" } + { "label": "Sorting", "to": "framework/svelte/examples/sorting" } ] }, { "label": "vue", "children": [ + { "label": "Kitchen Sink (All Features)", "to": "framework/vue/examples/kitchen-sink" }, { "label": "Column Filters", "to": "framework/vue/examples/filters" }, { "label": "Column Filters (Faceted)", "to": "framework/vue/examples/filters-faceted" }, { "label": "Fuzzy Search Filters", "to": "framework/vue/examples/filters-fuzzy" }, @@ -998,17 +1004,18 @@ { "label": "Performant Column Resizing", "to": "framework/vue/examples/column-resizing-performant" }, { "label": "Column Visibility", "to": "framework/vue/examples/column-visibility" }, { "label": "Expanding", "to": "framework/vue/examples/expanding" }, + { "label": "Expanding Sub Components", "to": "framework/vue/examples/sub-components" }, { "label": "Grouping", "to": "framework/vue/examples/grouping" }, { "label": "Pagination", "to": "framework/vue/examples/pagination" }, { "label": "Row Pinning", "to": "framework/vue/examples/row-pinning" }, { "label": "Row Selection", "to": "framework/vue/examples/row-selection" }, - { "label": "Sorting", "to": "framework/vue/examples/sorting" }, - { "label": "Kitchen Sink (All Features)", "to": "framework/vue/examples/kitchen-sink" } + { "label": "Sorting", "to": "framework/vue/examples/sorting" } ] }, { "label": "preact", "children": [ + { "label": "Kitchen Sink (All Features)", "to": "framework/preact/examples/kitchen-sink" }, { "label": "Column Filters", "to": "framework/preact/examples/filters" }, { "label": "Column Filters (Faceted)", "to": "framework/preact/examples/filters-faceted" }, { "label": "Fuzzy Search Filters", "to": "framework/preact/examples/filters-fuzzy" }, @@ -1021,12 +1028,12 @@ { "label": "Performant Column Resizing", "to": "framework/preact/examples/column-resizing-performant" }, { "label": "Column Visibility", "to": "framework/preact/examples/column-visibility" }, { "label": "Expanding", "to": "framework/preact/examples/expanding" }, + { "label": "Expanding Sub Components", "to": "framework/preact/examples/sub-components" }, { "label": "Grouping", "to": "framework/preact/examples/grouping" }, { "label": "Pagination", "to": "framework/preact/examples/pagination" }, { "label": "Row Pinning", "to": "framework/preact/examples/row-pinning" }, { "label": "Row Selection", "to": "framework/preact/examples/row-selection" }, - { "label": "Sorting", "to": "framework/preact/examples/sorting" }, - { "label": "Kitchen Sink (All Features)", "to": "framework/preact/examples/kitchen-sink" } + { "label": "Sorting", "to": "framework/preact/examples/sorting" } ] }, { @@ -1045,9 +1052,8 @@ { "label": "angular", "children": [ - { "label": "Composable Tables", "to": "framework/angular/examples/composable-tables" }, + { "label": "Composable Tables (createTableHook)", "to": "framework/angular/examples/composable-tables" }, { "label": "Custom Plugin", "to": "framework/angular/examples/custom-plugin" }, - { "label": "Sub Components", "to": "framework/angular/examples/sub-components" }, { "label": "With TanStack Virtual - Columns", "to": "framework/angular/examples/virtualized-columns" }, { "label": "With TanStack Virtual - Rows", "to": "framework/angular/examples/virtualized-rows" }, { "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/angular/examples/virtualized-infinite-scrolling" }, @@ -1063,8 +1069,7 @@ { "label": "lit", "children": [ - { "label": "Composable Tables", "to": "framework/lit/examples/composable-tables" }, - { "label": "Sub Components", "to": "framework/lit/examples/sub-components" }, + { "label": "Composable Tables (createTableHook)", "to": "framework/lit/examples/composable-tables" }, { "label": "With TanStack Virtual - Columns", "to": "framework/lit/examples/virtualized-columns" }, { "label": "With TanStack Virtual - Rows", "to": "framework/lit/examples/virtualized-rows" }, { "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/lit/examples/virtualized-infinite-scrolling" } @@ -1073,9 +1078,8 @@ { "label": "react", "children": [ - { "label": "Composable Tables", "to": "framework/react/examples/composable-tables" }, + { "label": "Composable Tables (createTableHook)", "to": "framework/react/examples/composable-tables" }, { "label": "Custom Plugin", "to": "framework/react/examples/custom-plugin" }, - { "label": "Sub Components", "to": "framework/react/examples/sub-components" }, { "label": "With TanStack Virtual - Columns", "to": "framework/react/examples/virtualized-columns" }, { "label": "With TanStack Virtual - Columns (Exp)", "to": "framework/react/examples/virtualized-columns-experimental" }, { "label": "With TanStack Virtual - Rows", "to": "framework/react/examples/virtualized-rows" }, @@ -1089,8 +1093,7 @@ { "label": "solid", "children": [ - { "label": "Composable Tables", "to": "framework/solid/examples/composable-tables" }, - { "label": "Sub Components", "to": "framework/solid/examples/sub-components" }, + { "label": "Composable Tables (createTableHook)", "to": "framework/solid/examples/composable-tables" }, { "label": "With TanStack Virtual - Columns", "to": "framework/solid/examples/virtualized-columns" }, { "label": "With TanStack Virtual - Rows", "to": "framework/solid/examples/virtualized-rows" }, { "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/solid/examples/virtualized-infinite-scrolling" }, @@ -1102,8 +1105,7 @@ { "label": "svelte", "children": [ - { "label": "Composable Tables", "to": "framework/svelte/examples/composable-tables" }, - { "label": "Sub Components", "to": "framework/svelte/examples/sub-components" }, + { "label": "Composable Tables (createTableHook)", "to": "framework/svelte/examples/composable-tables" }, { "label": "With TanStack Virtual - Columns", "to": "framework/svelte/examples/virtualized-columns" }, { "label": "With TanStack Virtual - Rows", "to": "framework/svelte/examples/virtualized-rows" }, { "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/svelte/examples/virtualized-infinite-scrolling" }, @@ -1114,8 +1116,7 @@ { "label": "vue", "children": [ - { "label": "Composable Tables", "to": "framework/vue/examples/composable-tables" }, - { "label": "Sub Components", "to": "framework/vue/examples/sub-components" }, + { "label": "Composable Tables (createTableHook)", "to": "framework/vue/examples/composable-tables" }, { "label": "With TanStack Virtual - Columns", "to": "framework/vue/examples/virtualized-columns" }, { "label": "With TanStack Virtual - Rows", "to": "framework/vue/examples/virtualized-rows" }, { "label": "With TanStack Virtual - Infinite Scrolling", "to": "framework/vue/examples/virtualized-infinite-scrolling" }, @@ -1126,9 +1127,8 @@ { "label": "preact", "children": [ - { "label": "Composable Tables", "to": "framework/preact/examples/composable-tables" }, + { "label": "Composable Tables (createTableHook)", "to": "framework/preact/examples/composable-tables" }, { "label": "Custom Plugin", "to": "framework/preact/examples/custom-plugin" }, - { "label": "Sub Components", "to": "framework/preact/examples/sub-components" }, { "label": "With TanStack Query", "to": "framework/preact/examples/with-tanstack-query" } ] } From a471632a3c9d3a9b01a615daafbdb94b7ca15eaa Mon Sep 17 00:00:00 2001 From: Kevin Van Cott Date: Wed, 24 Jun 2026 12:21:07 -0500 Subject: [PATCH 2/2] docs: improve composable table guides --- .../angular/guide/composable-tables.md | 152 ++++++++++++++++- docs/framework/lit/guide/composable-tables.md | 161 +++++++++++++++++- .../preact/guide/composable-tables.md | 159 ++++++++++++++++- .../react/guide/composable-tables.md | 159 ++++++++++++++++- .../solid/guide/composable-tables.md | 158 ++++++++++++++++- .../svelte/guide/composable-tables.md | 158 ++++++++++++++++- docs/framework/vue/guide/composable-tables.md | 142 ++++++++++++++- 7 files changed, 1026 insertions(+), 63 deletions(-) diff --git a/docs/framework/angular/guide/composable-tables.md b/docs/framework/angular/guide/composable-tables.md index 29a93b87b8..c08ada4a5c 100644 --- a/docs/framework/angular/guide/composable-tables.md +++ b/docs/framework/angular/guide/composable-tables.md @@ -1,17 +1,147 @@ --- -title: Composable Tables Guide +title: Composable Tables (createTableHook) Guide --- -Composable tables are app-level table factories built with `createTableHook`. Instead of repeating the same features, row models, default options, and table/cell/header components in every Angular table, you define that shared infrastructure once and consume it from each table component. +`createTableHook` creates an app-specific table factory. Use it to define shared features, row models, and default table options once, then create each Angular table with the columns and data that are unique to that table. -Use this pattern when multiple tables in an Angular app share behavior or rendering conventions. For a single isolated table, `injectTable` is usually enough. +The same API can also register reusable table, cell, and header components, but component registration is optional. Start with shared options and features first; add reusable components only when your app needs standardized table UI pieces. ## Examples -- [Composable Tables](../examples/composable-tables) - Two tables sharing one app table setup from `src/app/table.ts`. - [Basic App Table](../examples/basic-app-table) - Minimal `createTableHook` usage without the larger component registry. +- [Composable Tables](../examples/composable-tables) - Richer Users and Products tables sharing `src/app/table.ts` and reusable components. -## Setup +## Start With Shared Features and Options + +Create one app table hook and put the feature set, row models, and shared defaults there. This example makes sorting available to every table created by `injectAppTable`. + +```ts +import { + createSortedRowModel, + createTableHook, + rowSortingFeature, + sortFns, + tableFeatures, +} from '@tanstack/angular-table' + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) + +const { injectAppTable, createAppColumnHelper } = createTableHook({ + features, + debugTable: true, + enableSortingRemoval: false, +}) +``` + +Options passed to `createTableHook` become defaults for every table created by `injectAppTable`. The `features` option is also bound to the returned column helper, so column definitions know that sorting APIs are available. + +## Create App Columns + +Create one column helper per row type. The helper is already bound to your app's feature set, so each table does not need to thread `typeof features` through its column definitions. + +```ts +type Person = { + firstName: string + lastName: string + age: number + visits: number +} + +const columnHelper = createAppColumnHelper() + +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: () => 'Last Name', + cell: (info) => info.getValue(), + }), + columnHelper.accessor('age', { + header: 'Age', + }), + columnHelper.accessor('visits', { + header: 'Visits', + }), +]) +``` + +## Create A Table + +Create each table with `injectAppTable`. The call site provides table-specific inputs such as `columns` and `data`; shared features and defaults come from the hook. + +```ts +export class UsersTable { + readonly data = signal>([]) + + readonly table = injectAppTable(() => ({ + key: 'users-table', + columns, + data: this.data(), + })) +} +``` + +## Render With The Normal Table APIs + +You can render the table with the same table instance APIs used by a standalone `injectTable` table. This simple path does not require `appCell`, `appHeader`, `appFooter`, or registered components. + +```html + + + @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) { + + @for (header of headerGroup.headers; track header.id) { + + } + + } + + + @for (row of table.getRowModel().rows; track row.id) { + + @for (cell of row.getAllCells(); track cell.id) { + + } + + } + +
+ @if (!header.isPlaceholder) { + + {{ headerCell }} + + } +
+ + {{ renderCell }} + +
+``` + +## Override Shared Defaults Per Table + +Options passed to `injectAppTable` override defaults from `createTableHook`. Use this for the few tables that need different behavior without creating a separate app hook. + +```ts +readonly table = injectAppTable(() => ({ + key: 'sortable-users-table', + columns, + data: this.data(), + enableSortingRemoval: true, +})) +``` + +## Optional: Reusable Components + +The richer composable-tables example also uses `createTableHook` as a component registry. Use this when several tables should share the same toolbar controls, cell renderers, header renderers, or footer renderers. + +### Component Registry Setup The composable tables example keeps the shared setup in `src/app/table.ts`. That file creates one app-specific table factory and exports the helpers used by the rest of the example. @@ -95,7 +225,7 @@ export const { This file is the source of truth for the feature set, row model pipeline, row IDs, and registered components used by both tables in the example. -## Returned Helpers +### Returned Helpers | Helper | Purpose | |---|---| @@ -105,7 +235,7 @@ This file is the source of truth for the feature set, row model pipeline, row ID | `injectTableCellContext` | Reads the current cell inside registered cell components like `TextCell`. | | `injectTableHeaderContext` | Reads the current header/footer inside registered header components like `SortIndicator`. | -## Columns +### Component Columns Use `createAppColumnHelper()` instead of the base column helper when column definitions should render registered components. @@ -132,7 +262,7 @@ readonly columns = personColumnHelper.columns([ The registered components are available through the enhanced `cell` and `header` objects because the column helper is bound to the `createTableHook` configuration. -## Table Rendering +### Component Table Rendering Create each table with `injectAppTable`. Per-table options provide the data and columns; shared features and row models come from `src/app/table.ts`. @@ -179,8 +309,12 @@ In templates, use the Angular rendering helpers with the app wrappers: } ``` -## Reusing The Hook +### Reusing The Component Registry The example has separate Users and Products table components. Both import `createAppColumnHelper` and `injectAppTable` from `src/app/table.ts`, so they share sorting, filtering, pagination, row IDs, toolbar controls, cell renderers, and header/footer renderers while keeping their own data and columns. If different product areas need incompatible defaults, create another `createTableHook` setup file and export a second set of app helpers from there. + +## When To Use This Pattern + +Use `createTableHook` when multiple tables should share features, row models, default options, or conventions. Use the standalone `injectTable` API for a one-off table. Add the component registry only when the app wants standardized reusable table UI pieces. diff --git a/docs/framework/lit/guide/composable-tables.md b/docs/framework/lit/guide/composable-tables.md index 34712a46d2..167edf0863 100644 --- a/docs/framework/lit/guide/composable-tables.md +++ b/docs/framework/lit/guide/composable-tables.md @@ -1,17 +1,156 @@ --- -title: Composable Tables Guide +title: Composable Tables (createTableHook) Guide --- -Composable tables are app-level table factories built with `createTableHook`. They let Lit apps define shared features, row models, default options, and reusable render helpers once, then create multiple tables from that setup. +`createTableHook` creates an app-specific table factory. Use it to define shared features, row models, and default table options once, then create each Lit table with the columns and data that are unique to that table. -Use this pattern when several tables should share behavior and cell/header rendering conventions. For one standalone Lit table, `TableController` is usually enough. +The same API can also register reusable table, cell, and header render helpers, but component registration is optional. Start with shared options and features first; add reusable render helpers only when your app needs standardized table UI pieces. ## Examples -- [Composable Tables](../examples/composable-tables) - Users and Products tables sharing `src/hooks/table.ts`. - [Basic App Table](../examples/basic-app-table) - Minimal `createTableHook` setup. +- [Composable Tables](../examples/composable-tables) - Richer Users and Products tables sharing `src/hooks/table.ts` and reusable render helpers. -## Setup +## Start With Shared Features and Options + +Create one app table hook and put the feature set, row models, and shared defaults there. This example makes sorting available to every table created by `useAppTable`. + +```ts +import { + createSortedRowModel, + createTableHook, + rowSortingFeature, + sortFns, + tableFeatures, +} from '@tanstack/lit-table' + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) + +const { useAppTable, createAppColumnHelper } = createTableHook({ + features, + debugTable: true, + enableSortingRemoval: false, +}) +``` + +Options passed to `createTableHook` become defaults for every table created by `useAppTable`. The `features` option is also bound to the returned column helper, so column definitions know that sorting APIs are available. + +## Create App Columns + +Create one column helper per row type. The helper is already bound to your app's feature set, so each table does not need to thread `typeof features` through its column definitions. + +```ts +type Person = { + firstName: string + lastName: string + age: number + visits: number +} + +const columnHelper = createAppColumnHelper() + +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: 'Last Name', + cell: (info) => info.getValue(), + }), + columnHelper.accessor('age', { + header: 'Age', + }), + columnHelper.accessor('visits', { + header: 'Visits', + }), +]) +``` + +## Create A Table + +Create each table with `useAppTable`. The call site provides table-specific inputs such as `columns` and `data`; shared features and defaults come from the hook. + +```ts +private appTable = (() => { + const host = this + return useAppTable( + this, + { + columns, + get data() { + return host.data + }, + }, + (state) => ({ sorting: state.sorting }), + ) +})() +``` + +## Render With The Normal Table APIs + +You can render the table with the same table instance APIs used by a standalone `TableController` table. This simple path does not require `AppCell`, `AppHeader`, `AppFooter`, or registered render helpers. + +```ts +const table = this.appTable.table() + +return html` + + + ${table.getHeaderGroups().map( + (headerGroup) => html` + + ${headerGroup.headers.map( + (header) => html` + + `, + )} + + `, + )} + + + ${table.getRowModel().rows.map( + (row) => html` + + ${row + .getAllCells() + .map((cell) => html``)} + + `, + )} + +
+ ${header.isPlaceholder ? null : FlexRender({ header })} +
${FlexRender({ cell })}
+` +``` + +## Override Shared Defaults Per Table + +Options passed to `useAppTable` override defaults from `createTableHook`. Use this for the few tables that need different behavior without creating a separate app hook. + +```ts +const host = this + +const table = useAppTable(this, { + columns, + get data() { + return host.data + }, + enableSortingRemoval: true, +}) +``` + +## Optional: Reusable Components + +The richer composable-tables example also uses `createTableHook` as a component registry. Use this when several tables should share the same toolbar controls, cell renderers, header renderers, or footer renderers. + +### Component Registry Setup The composable tables example keeps the shared configuration in `src/hooks/table.ts`. @@ -80,7 +219,7 @@ export const { createAppColumnHelper, useAppTable, useTableContext } = The Lit example does not register `tableComponents` in `createTableHook`. Its table-level controls are custom elements that call `useTableContext(this)`, so they consume table context directly. -## Returned Helpers +### Returned Helpers | Helper | Purpose | |---|---| @@ -88,7 +227,7 @@ The Lit example does not register `tableComponents` in `createTableHook`. Its ta | `createAppColumnHelper` | Creates column helpers with `TFeatures` and registered cell/header component types already bound. | | `useTableContext` | Lets custom elements like `pagination-controls` read the nearest app table context. | -## Columns +### Component Columns Create one column helper per row type. Cell/header components in Lit are functions, so column definitions call the registered function on the enhanced `cell` or `header`. @@ -114,7 +253,7 @@ const columns = personColumnHelper.columns([ ]) ``` -## Table Rendering +### Component Table Rendering Call `useAppTable(this, options, selector)` from the `LitElement` host. The helper returns an object with `table()`, which computes the current app table through the controller. @@ -187,6 +326,10 @@ return html` ` ``` -## Reusing The Hook +### Reusing The Component Registry The Users and Products table elements import the same `createAppColumnHelper` and `useAppTable` from `src/hooks/table.ts`. Their data and columns differ, but sorting, filtering, pagination, row IDs, and registered cell/header renderers come from one shared configuration. + +## When To Use This Pattern + +Use `createTableHook` when multiple tables should share features, row models, default options, or conventions. Use the standalone `TableController` API for a one-off table. Add the component registry only when the app wants standardized reusable table UI pieces. diff --git a/docs/framework/preact/guide/composable-tables.md b/docs/framework/preact/guide/composable-tables.md index 5aab1684bb..dc4f906ece 100644 --- a/docs/framework/preact/guide/composable-tables.md +++ b/docs/framework/preact/guide/composable-tables.md @@ -1,17 +1,154 @@ --- -title: Composable Tables Guide +title: Composable Tables (createTableHook) Guide --- -Composable tables are app-level table factories built with `createTableHook`. They let Preact apps define shared features, row models, default options, and reusable components once, then create multiple tables from that shared setup. +`createTableHook` creates an app-specific table factory. Use it to define shared features, row models, and default table options once, then create each Preact table with the columns and data that are unique to that table. -Use this pattern when several tables should share behavior and rendering conventions. For one standalone table, `useTable` is usually enough. +The same API can also register reusable table, cell, and header components, but component registration is optional. Start with shared options and features first; add reusable components only when your app needs standardized table UI pieces. ## Examples -- [Composable Tables](../examples/composable-tables) - Users and Products tables sharing `src/hooks/table.ts`. - [Basic useAppTable](../examples/basic-use-app-table) - Minimal `createTableHook` setup. +- [Composable Tables](../examples/composable-tables) - Richer Users and Products tables sharing `src/hooks/table.ts` and reusable components. -## Setup +## Start With Shared Features and Options + +Create one app table hook and put the feature set, row models, and shared defaults there. This example makes sorting available to every table created by `useAppTable`. + +```tsx +import { + createSortedRowModel, + createTableHook, + rowSortingFeature, + sortFns, + tableFeatures, +} from '@tanstack/preact-table' + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) + +const { useAppTable, createAppColumnHelper } = createTableHook({ + features, + debugTable: true, + enableSortingRemoval: false, +}) +``` + +Options passed to `createTableHook` become defaults for every table created by `useAppTable`. The `features` option is also bound to the returned column helper, so column definitions know that sorting APIs are available. + +## Create App Columns + +Create one column helper per row type. The helper is already bound to your app's feature set, so each table does not need to thread `typeof features` through its column definitions. + +```tsx +type Person = { + firstName: string + lastName: string + age: number + visits: number +} + +const columnHelper = createAppColumnHelper() + +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: () => Last Name, + cell: (info) => {info.getValue()}, + }), + columnHelper.accessor('age', { + header: 'Age', + }), + columnHelper.accessor('visits', { + header: 'Visits', + }), +]) +``` + +## Create A Table + +Create each table with `useAppTable`. The call site provides table-specific inputs such as `columns` and `data`; shared features and defaults come from the hook. + +```tsx +function UsersTable({ data }: { data: Person[] }) { + const table = useAppTable( + { + key: 'users-table', + columns, + data, + }, + (state) => ({ sorting: state.sorting }), + ) + + // render with the table instance +} +``` + +## Render With The Normal Table APIs + +You can render the table with the same table instance APIs used by a standalone `useTable` table. This simple path does not require `AppTable`, `AppCell`, `AppHeader`, or registered components. + +```tsx +return ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getAllCells().map((cell) => ( + + ))} + + ))} + +
+ {header.isPlaceholder ? null : ( + + )} +
+ +
+) +``` + +## Override Shared Defaults Per Table + +Options passed to `useAppTable` override defaults from `createTableHook`. Use this for the few tables that need different behavior without creating a separate app hook. + +```tsx +const table = useAppTable( + { + key: 'sortable-users-table', + columns, + data, + enableSortingRemoval: true, + }, + (state) => ({ sorting: state.sorting }), +) +``` + +## Optional: Reusable Components + +The richer composable-tables example also uses `createTableHook` as a component registry. Use this when several tables should share the same toolbar controls, cell renderers, header renderers, or footer renderers. + +### Component Registry Setup The composable tables example keeps the shared configuration in `src/hooks/table.ts`. @@ -93,7 +230,7 @@ export const { }) ``` -## Returned Helpers +### Returned Helpers | Helper | Purpose | |---|---| @@ -103,7 +240,7 @@ export const { | `useCellContext` | Reads the current cell inside registered cell components. | | `useHeaderContext` | Reads the current header/footer inside registered header components. | -## Columns +### Component Columns Create one column helper per row type. The helper is bound to the app table setup, so column definitions can reference registered components directly. @@ -135,7 +272,7 @@ const columns = useMemo( Registered cell components use `useCellContext()` internally, and registered header/footer components use `useHeaderContext()`. -## Table Rendering +### Component Table Rendering Create each table with `useAppTable`. You pass table-specific options like `key`, `columns`, and `data`; the shared `features` (which includes row model factories), `getRowId`, and component registries come from the hook. @@ -207,6 +344,10 @@ The returned table includes `AppTable`, `AppHeader`, `AppCell`, and `AppFooter` ``` -## Reusing The Hook +### Reusing The Component Registry The example creates both `personColumnHelper` and `productColumnHelper` from the same `createAppColumnHelper`, then renders Users and Products tables with the same `useAppTable` factory. Each table owns its data and columns, while the app hook owns table infrastructure and component conventions. + +## When To Use This Pattern + +Use `createTableHook` when multiple tables should share features, row models, default options, or conventions. Use the standalone `useTable` API for a one-off table. Add the component registry only when the app wants standardized reusable table UI pieces. diff --git a/docs/framework/react/guide/composable-tables.md b/docs/framework/react/guide/composable-tables.md index 0930c7a7bd..53e7b51b19 100644 --- a/docs/framework/react/guide/composable-tables.md +++ b/docs/framework/react/guide/composable-tables.md @@ -1,17 +1,154 @@ --- -title: Composable Tables Guide +title: Composable Tables (createTableHook) Guide --- -Composable tables are app-level table factories built with `createTableHook`. They let React apps define shared features, row models, default options, and reusable components once, then create multiple tables from that shared setup. +`createTableHook` creates an app-specific table factory. Use it to define shared features, row models, and default table options once, then create each React table with the columns and data that are unique to that table. -Use this pattern when several tables should share the same behavior and component conventions. For one standalone table, `useTable` is usually enough. +The same API can also register reusable table, cell, and header components, but component registration is optional. Start with shared options and features first; add reusable components only when your app needs standardized table UI pieces. ## Examples -- [Composable Tables](../examples/composable-tables) - Users and Products tables sharing `src/hooks/table.ts`. - [Basic useAppTable](../examples/basic-use-app-table) - Minimal `createTableHook` setup. +- [Composable Tables](../examples/composable-tables) - Richer Users and Products tables sharing `src/hooks/table.ts` and reusable components. -## Setup +## Start With Shared Features and Options + +Create one app table hook and put the feature set, row models, and shared defaults there. This example makes sorting available to every table created by `useAppTable`. + +```tsx +import { + createSortedRowModel, + createTableHook, + rowSortingFeature, + sortFns, + tableFeatures, +} from '@tanstack/react-table' + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) + +const { useAppTable, createAppColumnHelper } = createTableHook({ + features, + debugTable: true, + enableSortingRemoval: false, +}) +``` + +Options passed to `createTableHook` become defaults for every table created by `useAppTable`. The `features` option is also bound to the returned column helper, so column definitions know that sorting APIs are available. + +## Create App Columns + +Create one column helper per row type. The helper is already bound to your app's feature set, so each table does not need to thread `typeof features` through its column definitions. + +```tsx +type Person = { + firstName: string + lastName: string + age: number + visits: number +} + +const columnHelper = createAppColumnHelper() + +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: () => Last Name, + cell: (info) => {info.getValue()}, + }), + columnHelper.accessor('age', { + header: 'Age', + }), + columnHelper.accessor('visits', { + header: 'Visits', + }), +]) +``` + +## Create A Table + +Create each table with `useAppTable`. The call site provides table-specific inputs such as `columns` and `data`; shared features and defaults come from the hook. + +```tsx +function UsersTable({ data }: { data: Person[] }) { + const table = useAppTable( + { + key: 'users-table', + columns, + data, + }, + (state) => ({ sorting: state.sorting }), + ) + + // render with the table instance +} +``` + +## Render With The Normal Table APIs + +You can render the table with the same table instance APIs used by a standalone `useTable` table. This simple path does not require `AppTable`, `AppCell`, `AppHeader`, or registered components. + +```tsx +return ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getAllCells().map((cell) => ( + + ))} + + ))} + +
+ {header.isPlaceholder ? null : ( + + )} +
+ +
+) +``` + +## Override Shared Defaults Per Table + +Options passed to `useAppTable` override defaults from `createTableHook`. Use this for the few tables that need different behavior without creating a separate app hook. + +```tsx +const table = useAppTable( + { + key: 'sortable-users-table', + columns, + data, + enableSortingRemoval: true, + }, + (state) => ({ sorting: state.sorting }), +) +``` + +## Optional: Reusable Components + +The richer composable-tables example also uses `createTableHook` as a component registry. Use this when several tables should share the same toolbar controls, cell renderers, header renderers, or footer renderers. + +### Component Registry Setup The composable tables example keeps the shared configuration in `src/hooks/table.ts`. @@ -93,7 +230,7 @@ export const { }) ``` -## Returned Helpers +### Returned Helpers | Helper | Purpose | |---|---| @@ -103,7 +240,7 @@ export const { | `useCellContext` | Reads the current cell inside registered cell components. | | `useHeaderContext` | Reads the current header/footer inside registered header components. | -## Columns +### Component Columns Create one column helper per row type. Since the helper is already bound to the app table setup, column definitions can reference registered cell and header components directly. @@ -135,7 +272,7 @@ const columns = useMemo( Registered cell components use `useCellContext()` internally, and registered header/footer components use `useHeaderContext()`. -## Table Rendering +### Component Table Rendering Create each table with `useAppTable`. You pass table-specific options like `key`, `columns`, and `data`; the shared `features`, `getRowId`, and component registries come from the hook. @@ -207,6 +344,10 @@ The returned table includes `AppTable`, `AppHeader`, `AppCell`, and `AppFooter` ``` -## Reusing The Hook +### Reusing The Component Registry The example creates both `personColumnHelper` and `productColumnHelper` from the same `createAppColumnHelper`, then renders Users and Products tables with the same `useAppTable` factory. Each table owns its data and columns, while the app hook owns table infrastructure and component conventions. + +## When To Use This Pattern + +Use `createTableHook` when multiple tables should share features, row models, default options, or conventions. Use the standalone `useTable` API for a one-off table. Add the component registry only when the app wants standardized reusable table UI pieces. diff --git a/docs/framework/solid/guide/composable-tables.md b/docs/framework/solid/guide/composable-tables.md index 2397ab6d2d..9a275b9bc8 100644 --- a/docs/framework/solid/guide/composable-tables.md +++ b/docs/framework/solid/guide/composable-tables.md @@ -1,17 +1,153 @@ --- -title: Composable Tables Guide +title: Composable Tables (createTableHook) Guide --- -Composable tables are app-level table factories built with `createTableHook`. They let Solid apps define shared features, row models, default options, and reusable JSX components once, then create multiple tables from that shared setup. +`createTableHook` creates an app-specific table factory. Use it to define shared features, row models, and default table options once, then create each Solid table with the columns and data that are unique to that table. -Use this pattern when several tables should share behavior and rendering conventions. For one standalone table, `createTable` is usually enough. +The same API can also register reusable table, cell, and header components, but component registration is optional. Start with shared options and features first; add reusable components only when your app needs standardized table UI pieces. ## Examples -- [Composable Tables](../examples/composable-tables) - Users and Products tables sharing `src/hooks/table.ts`. - [Basic App Table](../examples/basic-app-table) - Minimal `createTableHook` setup. +- [Composable Tables](../examples/composable-tables) - Richer Users and Products tables sharing `src/hooks/table.ts` and reusable components. -## Setup +## Start With Shared Features and Options + +Create one app table hook and put the feature set, row models, and shared defaults there. This example makes sorting available to every table created by `createAppTable`. + +```tsx +import { + createSortedRowModel, + createTableHook, + rowSortingFeature, + sortFns, + tableFeatures, +} from '@tanstack/solid-table' + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) + +const { createAppTable, createAppColumnHelper } = createTableHook({ + features, + debugTable: true, + enableSortingRemoval: false, +}) +``` + +Options passed to `createTableHook` become defaults for every table created by `createAppTable`. The `features` option is also bound to the returned column helper, so column definitions know that sorting APIs are available. + +## Create App Columns + +Create one column helper per row type. The helper is already bound to your app's feature set, so each table does not need to thread `typeof features` through its column definitions. + +```tsx +type Person = { + firstName: string + lastName: string + age: number + visits: number +} + +const columnHelper = createAppColumnHelper() + +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: () => Last Name, + cell: (info) => {info.getValue()}, + }), + columnHelper.accessor('age', { + header: 'Age', + }), + columnHelper.accessor('visits', { + header: 'Visits', + }), +]) +``` + +## Create A Table + +Create each table with `createAppTable`. The call site provides table-specific inputs such as `columns` and reactive `data`; shared features and defaults come from the hook. + +```tsx +const [data, setData] = createSignal>([]) + +const table = createAppTable({ + key: 'users-table', + columns, + get data() { + return data() + }, +}) +``` + +## Render With The Normal Table APIs + +You can render the table with the same table instance APIs used by a standalone `createTable` table. This simple path does not require `AppTable`, `AppCell`, `AppHeader`, or registered components. + +```tsx +return ( + + + + {(headerGroup) => ( + + + {(header) => ( + + )} + + + )} + + + + + {(row) => ( + + + {(cell) => ( + + )} + + + )} + + +
+ +
+ +
+) +``` + +## Override Shared Defaults Per Table + +Options passed to `createAppTable` override defaults from `createTableHook`. Use this for the few tables that need different behavior without creating a separate app hook. + +```tsx +const table = createAppTable({ + key: 'sortable-users-table', + columns, + get data() { + return data() + }, + enableSortingRemoval: true, +}) +``` + +## Optional: Reusable Components + +The richer composable-tables example also uses `createTableHook` as a component registry. Use this when several tables should share the same toolbar controls, cell renderers, header renderers, or footer renderers. + +### Component Registry Setup The composable tables example keeps the shared configuration in `src/hooks/table.ts`. @@ -93,7 +229,7 @@ export const { }) ``` -## Returned Helpers +### Returned Helpers | Helper | Purpose | |---|---| @@ -103,7 +239,7 @@ export const { | `useCellContext` | Reads the current cell inside registered cell components. | | `useHeaderContext` | Reads the current header/footer inside registered header components. | -## Columns +### Component Columns Create one column helper per row type. Since the helper is bound to the app setup, registered JSX components are available on cell and header contexts. @@ -129,7 +265,7 @@ const columns = personColumnHelper.columns([ ]) ``` -## Table Rendering +### Component Table Rendering Create each table with `createAppTable`. You provide table-specific options like `key`, `columns`, and reactive data; the shared table infrastructure comes from the hook. @@ -214,6 +350,10 @@ The returned table includes JSX wrappers. The example uses `table.AppTable` with ``` -## Reusing The Hook +### Reusing The Component Registry The example creates `personColumnHelper` and `productColumnHelper` from the same `createAppColumnHelper`, then creates both Users and Products tables with `createAppTable`. Each table keeps its own signals and columns, while the shared hook owns features, row models, row IDs, and component conventions. + +## When To Use This Pattern + +Use `createTableHook` when multiple tables should share features, row models, default options, or conventions. Use the standalone `createTable` API for a one-off table. Add the component registry only when the app wants standardized reusable table UI pieces. diff --git a/docs/framework/svelte/guide/composable-tables.md b/docs/framework/svelte/guide/composable-tables.md index 2d15ac68f5..f5673991c9 100644 --- a/docs/framework/svelte/guide/composable-tables.md +++ b/docs/framework/svelte/guide/composable-tables.md @@ -1,17 +1,153 @@ --- -title: Composable Tables Guide +title: Composable Tables (createTableHook) Guide --- -Composable tables are app-level table factories built with `createTableHook`. They let Svelte apps define shared features, row models, default options, and reusable Svelte components once, then create multiple tables from that setup. +`createTableHook` creates an app-specific table factory. Use it to define shared features, row models, and default table options once, then create each Svelte table with the columns and data that are unique to that table. -Use this pattern when several tables should share behavior and rendering conventions. For one standalone table, `createTable` is usually enough. +The same API can also register reusable table, cell, and header components, but component registration is optional. Start with shared options and features first; add reusable components only when your app needs standardized table UI pieces. ## Examples -- [Composable Tables](../examples/composable-tables) - Users and Products tables sharing `src/hooks/table.ts`. - [Basic App Table](../examples/basic-app-table) - Minimal `createTableHook` setup. +- [Composable Tables](../examples/composable-tables) - Richer Users and Products tables sharing `src/hooks/table.ts` and reusable components. -## Setup +## Start With Shared Features and Options + +Create one app table hook and put the feature set, row models, and shared defaults there. This example makes sorting available to every table created by `createAppTable`. + +```svelte + +``` + +Options passed to `createTableHook` become defaults for every table created by `createAppTable`. The `features` option is also bound to the returned column helper, so column definitions know that sorting APIs are available. + +## Create App Columns + +Create one column helper per row type. The helper is already bound to your app's feature set, so each table does not need to thread `typeof features` through its column definitions. + +```svelte + +``` + +## Create A Table + +Create each table with `createAppTable`. The call site provides table-specific inputs such as `columns` and reactive `data`; shared features and defaults come from the hook. + +```svelte + +``` + +## Render With The Normal Table APIs + +You can render the table with the same table instance APIs used by a standalone `createTable` table. This simple path does not require `AppTable`, `AppCell`, `AppHeader`, or registered components. + +```svelte + + + {#each table.getHeaderGroups() as headerGroup (headerGroup.id)} + + {#each headerGroup.headers as header (header.id)} + + {/each} + + {/each} + + + {#each table.getRowModel().rows as row (row.id)} + + {#each row.getAllCells() as cell (cell.id)} + + {/each} + + {/each} + +
+ {#if !header.isPlaceholder} + + {/if} +
+ +
+``` + +## Override Shared Defaults Per Table + +Options passed to `createAppTable` override defaults from `createTableHook`. Use this for the few tables that need different behavior without creating a separate app hook. + +```svelte + +``` + +## Optional: Reusable Components + +The richer composable-tables example also uses `createTableHook` as a component registry. Use this when several tables should share the same toolbar controls, cell renderers, header renderers, or footer renderers. + +### Component Registry Setup The composable tables example keeps the shared configuration in `src/hooks/table.ts`. @@ -87,7 +223,7 @@ export const { }) ``` -## Returned Helpers +### Returned Helpers | Helper | Purpose | |---|---| @@ -97,7 +233,7 @@ export const { | `useCellContext` | Reads the current cell inside registered cell components. | | `useHeaderContext` | Reads the current header/footer inside registered header components. | -## Columns +### Component Columns Create one column helper per row type. The Svelte example uses `renderComponent(...)` when a column def returns a registered Svelte component. @@ -124,7 +260,7 @@ Create one column helper per row type. The Svelte example uses `renderComponent( ``` -## Table Rendering +### Component Table Rendering Create each table with `createAppTable`. In Svelte 5, pass reactive data through a getter so table options read the current rune value. @@ -198,6 +334,10 @@ The returned table includes Svelte components for `AppTable`, `AppHeader`, `AppC ``` -## Reusing The Hook +### Reusing The Component Registry The Users and Products Svelte components import the same `createAppColumnHelper` and `createAppTable` from `src/hooks/table.ts`. Each component owns its `$state` data and columns, while the shared hook owns features, row models, row IDs, and the component registry. + +## When To Use This Pattern + +Use `createTableHook` when multiple tables should share features, row models, default options, or conventions. Use the standalone `createTable` API for a one-off table. Add the component registry only when the app wants standardized reusable table UI pieces. diff --git a/docs/framework/vue/guide/composable-tables.md b/docs/framework/vue/guide/composable-tables.md index 56479f0b22..a45bf3994f 100644 --- a/docs/framework/vue/guide/composable-tables.md +++ b/docs/framework/vue/guide/composable-tables.md @@ -1,17 +1,137 @@ --- -title: Composable Tables Guide +title: Composable Tables (createTableHook) Guide --- -Composable tables are app-level table factories built with `createTableHook`. They let Vue apps define shared features, row models, default options, and reusable components once, then create multiple tables from that setup. +`createTableHook` creates an app-specific table factory. Use it to define shared features, row models, and default table options once, then create each Vue table with the columns and data that are unique to that table. -Use this pattern when several tables should share behavior and rendering conventions. For one standalone table, `useTable` is usually enough. +The same API can also register reusable table, cell, and header components, but component registration is optional. Start with shared options and features first; add reusable components only when your app needs standardized table UI pieces. ## Examples -- [Composable Tables](../examples/composable-tables) - Users and Products tables sharing `src/hooks/table.ts`. - [Basic useAppTable](../examples/basic-use-app-table) - Minimal `createTableHook` setup. +- [Composable Tables](../examples/composable-tables) - Richer Users and Products tables sharing `src/hooks/table.ts` and reusable components. -## Setup +## Start With Shared Features and Options + +Create one app table hook and put the feature set, row models, and shared defaults there. This example makes sorting available to every table created by `useAppTable`. + +```ts +import { + createSortedRowModel, + createTableHook, + rowSortingFeature, + sortFns, + tableFeatures, +} from '@tanstack/vue-table' + +const features = tableFeatures({ + rowSortingFeature, + sortedRowModel: createSortedRowModel(), + sortFns, +}) + +const { useAppTable, createAppColumnHelper } = createTableHook({ + features, + debugTable: true, + enableSortingRemoval: false, +}) +``` + +Options passed to `createTableHook` become defaults for every table created by `useAppTable`. The `features` option is also bound to the returned column helper, so column definitions know that sorting APIs are available. + +## Create App Columns + +Create one column helper per row type. The helper is already bound to your app's feature set, so each table does not need to thread `typeof features` through its column definitions. + +```ts +type Person = { + firstName: string + lastName: string + age: number + visits: number +} + +const columnHelper = createAppColumnHelper() + +const columns = columnHelper.columns([ + columnHelper.accessor('firstName', { + cell: (info) => info.getValue(), + }), + columnHelper.accessor((row) => row.lastName, { + id: 'lastName', + header: 'Last Name', + cell: (info) => info.getValue(), + }), + columnHelper.accessor('age', { + header: 'Age', + }), + columnHelper.accessor('visits', { + header: 'Visits', + }), +]) +``` + +## Create A Table + +Create each table with `useAppTable`. The call site provides table-specific inputs such as `columns` and reactive `data`; shared features and defaults come from the hook. + +```ts +const data = ref>([]) + +const table = useAppTable({ + key: 'users-table', + columns, + data, +}) +``` + +## Render With The Normal Table APIs + +You can render the table with the same table instance APIs used by a standalone `useTable` table. This simple path does not require `AppTable`, `AppCell`, `AppHeader`, or registered components. + +```vue + +``` + +## Override Shared Defaults Per Table + +Options passed to `useAppTable` override defaults from `createTableHook`. Use this for the few tables that need different behavior without creating a separate app hook. + +```ts +const table = useAppTable({ + key: 'sortable-users-table', + columns, + data, + enableSortingRemoval: true, +}) +``` + +## Optional: Reusable Components + +The richer composable-tables example also uses `createTableHook` as a component registry. Use this when several tables should share the same toolbar controls, cell renderers, header renderers, or footer renderers. + +### Component Registry Setup The composable tables example keeps the shared configuration in `src/hooks/table.ts`. @@ -120,7 +240,7 @@ export const useHeaderContext: () => Header< > = _hook.useHeaderContext ``` -## Returned Helpers +### Returned Helpers | Helper | Purpose | |---|---| @@ -130,7 +250,7 @@ export const useHeaderContext: () => Header< | `useCellContext` | Reads the current cell inside registered cell components. | | `useHeaderContext` | Reads the current header/footer inside registered header components. | -## Columns +### Component Columns Create one column helper per row type. Vue registered components are returned from column definitions and then rendered through dynamic `` usage. @@ -159,7 +279,7 @@ const columns = columnHelper.columns([ ``` -## Table Rendering +### Component Table Rendering Create each table with `useAppTable`. Pass table-specific options like `key`, `columns`, reactive `data`, and any per-table state. @@ -228,6 +348,10 @@ The returned table includes Vue components for `AppTable`, `AppHeader`, `AppCell ``` -## Reusing The Hook +### Reusing The Component Registry The Users and Products Vue components import the same `createAppColumnHelper` and `useAppTable` from `src/hooks/table.ts`. Each component owns its refs and columns, while the shared hook owns features, row models, row IDs, table components, cell components, and header/footer components. + +## When To Use This Pattern + +Use `createTableHook` when multiple tables should share features, row models, default options, or conventions. Use the standalone `useTable` API for a one-off table. Add the component registry only when the app wants standardized reusable table UI pieces.