Référence DataTable
Référence des props et usages du composant DataTable unifié
Entrées natives
Données de démonstration. Les modifications restent dans cet aperçu.
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { NuqsAdapter } from "nuqs/adapters/react";
import { useState } from "react";
import { DataTable } from "@/components/ui/yayaw-table";
import type { ExamplePresentation } from "./presentation";
import { getTableActions } from "./product-actions";
import { getTableConfig } from "./product-config";
export default function Products(presentation: ExamplePresentation = {}) {
// Reuse the application's provider instead when it already owns a QueryClient.
const [queryClient] = useState(() => new QueryClient());
return (
<QueryClientProvider client={queryClient}>
<NuqsAdapter>
<DataTable
{...presentation}
getRowId={(row) => String(row.id)}
getTableActions={getTableActions}
getTableConfig={getTableConfig}
tableId="products-example"
tableType="products"
title={presentation.title ?? "Products"}
/>
</NuqsAdapter>
</QueryClientProvider>
);
}Données de démonstration. Les modifications restent dans cet aperçu.
<script setup lang="ts">
import type { ExamplePresentation } from "./presentation";
defineProps<ExamplePresentation>();
import { YayawDataTable } from "@/components/ui/yayaw-table-vue";
import { getTableActions } from "./product-actions";
import { productConfig } from "./product-config";
</script>
<template>
<YayawDataTable v-bind="$props"
:config="productConfig"
:get-table-actions="getTableActions"
:get-row-id="row => String(row.id)"
table-id="products-example"
table-type="products"
:title="title ?? 'Products'"
/>
</template>Référence DataTable
Composant point d'entrée unique : vous passez config + actions en props ; il compose le provider, l'UI et la logique de table en interne.
import { DataTable } from "@/components/ui/yayaw-table";Pour un setup minimal fonctionnel, il vous faut tableType, getTableConfig et getTableActions. Voir Provider & Setup pour la vue complète et le layout Next.js (NuqsAdapter, QueryClientProvider).
Si aucun QueryClient n'est disponible depuis le contexte (et qu'aucune prop queryClient explicite n'est fournie), DataTable lève maintenant une erreur runtime explicite.
Props
Liste des props disponibles pour configurer DataTable.
tableType
Type de configuration de table. Utilisé pour résoudre config et actions (getTableConfig("products"), getTableActions("products")).
<DataTable tableType="products" />Requis : true
tableId
Identifiant stable de l'instance de table pour l'état d'URL, le cache, la sélection, la pagination et la toolbar. Par défaut : tableType.
Utilisez-le quand une même table affiche plusieurs modèles métier mais doit garder un seul état partagé.
Type : string
instanceId
Isole cette instance sur une page qui contient d'autres tables : ses clés d'URL deviennent <instanceId>-view, <instanceId>-historyIndex et <instanceId>-<clé> au lieu de view, historyIndex et <tableId>-<clé>, et son état vit dans un store à elle. La config, les actions et les vues enregistrées restent résolues par table. Voir Plusieurs tables sur une page.
Type : string | Par défaut : undefined
initialView
La vue enregistrée (son id devient la vue active) ou la config de vue dont part une instance sans synchronisation d'URL (table.syncUrl: false), appliquée avant sa première requête. Ignorée quand la synchronisation d'URL est active ; utilisez alors initialActiveViewId. Pour les tables intégrées sans barre d'outils, comme les widgets de tableau de bord.
Type : { id?: string | null; config: TableViewConfig } | Par défaut : undefined
onViewConfigChange
Appelée avec la vue qu'affiche la table (canonicalViewConfig(view) : sa recherche, ses filtres, son tri, ses regroupements, ses colonnes, son mode d'affichage, les réglages de chaque mode et la taille de page, nettoyés comme ceux des vues enregistrées, sans les colonnes select et actions, clés dans un ordre unique) au démarrage de la table puis après chaque changement, jamais deux fois pour une même vue. L'éditeur de vue du tableau de bord et « Faire de la vue actuelle la vue par défaut de l'écran » la lisent. Vue émet view-config-change avec la même config et expose getViewConfig().
Type : (config: ViewConfig) => void | Par défaut : undefined
formType
Type de formulaire par défaut pour create/edit. Par défaut : tableType. La config de table peut le surcharger avec form.createFormType, form.editFormType ou form.resolveEditFormType(row).
Type : string
getTableConfig
Fonction qui retourne la configuration table/colonnes pour le tableType donné. Requise pour définir colonnes, tri, visibilité et options.
Type : (tableType: string) => Config | undefined
getTableActions
Fonction qui retourne les actions (list, create, update, delete, bulk, etc.) pour le tableType. list est obligatoire pour les données server-driven.
Type : (tableType: string) => TableActions | undefined
getFormConfig
Fonction qui retourne les définitions de champs pour les dialogs create/edit et bulk edit. Optionnelle, mais nécessaire pour les formulaires intégrés.
Type : (formType: string, ctx?: FormConfigContext) => FormConfig | undefined
ctx contient { mode, tableId, tableType, formType, row, initialData, values }, ce qui permet de changer les champs selon la ligne éditée ou les valeurs courantes du formulaire.
queryClient
Instance TanStack Query explicite optionnelle. Yayaw Table n'en crée plus en interne. Dans la plupart des apps, utilisez un QueryClientProvider partagé au niveau app et omettez cette prop.
Type : QueryClient
initialData
Lignes injectées dans le premier état client de la table. Utilisez cette prop quand un Server Component a déjà chargé la première page et que vous voulez hydrater la table avec des données utiles avant le refresh client TanStack Query. Sans elle, la table affiche son état de chargement dès le premier rendu, côté serveur aussi, jusqu’à la réponse de la première page, puis monte sa vue une seule fois avec ces lignes (depuis v3.9.1 ; React affichait d’abord une table vide, et montait la vue deux fois).
Type : Record<string, unknown>[] | Par défaut : []
initialPageCount
Nombre total de pages correspondant à initialData. Fournissez-le pour les jeux de données paginés côté serveur afin que le premier rendu garde les bons contrôles de pagination au lieu de supposer une seule page.
Type : number | Par défaut : 1 quand initialData est présent
initialRowCount
Nombre total de lignes correspondant à initialData. Fournissez-le quand le serveur connaît le total filtré complet de la première page.
Type : number | Par défaut : initialData.length
className
Classes additionnelles appliquées au wrapper racine.
Type : string | Par défaut : undefined
loadingOverlay
Surcouche de chargement personnalisée affiché pendant le chargement des données.
Type : React.ReactNode | Par défaut : spinner interne
<DataTable tableType="products" loadingOverlay={<MySpinner />} />onRowSelectionChange
Callback déclenché quand la sélection de lignes change.
Type : (rows: Row<Record<string, unknown>>[]) => void | Par défaut : undefined
activeRowId
Marque une ligne comme active. Utile pour les tables master-detail où la table est la surface d'inventaire et un autre panneau affiche la ligne sélectionnée.
Type : string | Par défaut : undefined
getRowId
Retourne un id de ligne stable pour le surlignage actif, la sélection et les métadonnées de ligne. Si omis, la table utilise les champs id courants puis l'id React Table.
Type : (row: Record<string, unknown>) => string | Par défaut : undefined
onRowActivate
Callback déclenché quand une zone non interactive de ligne ou de carte est cliquée et que le clic ouvre la vue enregistrement — table.rowClickMode: 'activate', ou le mode par défaut quand aucune édition ni aucun lien de ligne ne s'applique. Il s'exécute en plus de la vue enregistrement intégrée (voir Détails d'enregistrement) et ne la remplace pas ; passez details={false} pour vous appuyer uniquement sur onRowActivate.
Type : (row: Record<string, unknown>, event: React.MouseEvent) => void | Par défaut : undefined
onRowClick
Callback déclenché par les interactions de lien, y compris les clics de ligne en mode row-link et les boutons lien de Gallery. Utilisez-le pour router via votre app ou suivre de l'analytics au lieu de dépendre du comportement navigateur par défaut.
Type : (url: string, row: Record<string, unknown>, event: React.MouseEvent) => void | Par défaut : undefined
onBulkEdit
Gère l'édition de masse des lignes sélectionnées.
Type : (rows: Row<Record<string, unknown>>[]) => BulkActionResult | void | Promise<BulkActionResult | void> | Par défaut : actions provider
onBulkDelete
Gère la suppression de masse des lignes sélectionnées.
Type : (rows: Row<Record<string, unknown>>[]) => BulkActionResult | BulkDeleteExecutionOutcome | void | Promise<...> | Par défaut : provider bulkDelete / delete
onBulkCopy
Gère la duplication de masse des lignes sélectionnées.
Type : (rows: Row<Record<string, unknown>>[]) => BulkActionResult | void | Promise<BulkActionResult | void> | Par défaut : copie clipboard interne
onExport
Surcharge le comportement d'export de la barre d'outils. Appelé avec toutes les lignes correspondant à l'état courant (search/filters/sort).
Type : (rows: Record<string, unknown>[]) => void \| Promise<void> | Par défaut : export CSV interne
onBulkExport
Surcharge le comportement d'export bulk. Appelé avec les lignes sélectionnées.
Type : (rows: Row<Record<string, unknown>>[]) => void \| Promise<void> | Par défaut : export CSV interne
toolbarActions
Injecte des actions personnalisées dans la barre d'outils principale. Accepte un tableau statique ou une fonction recevant le contexte courant. Sur les grands écrans, elles s'affichent en boutons de la barre d'outils avant les Réglages de la vue ; en mode compact, elles rejoignent le menu Données, à côté d'Exporter, Connecter et Partager.
Type : ToolbarAction[] | ((ctx: ToolbarActionContext) => ToolbarAction[]) | Par défaut : undefined
toolbarActionsPlacement
Accepté pour la rétrocompatibilité. Ne change plus la position des actions personnalisées, puisqu'Exporter a rejoint le menu Données.
Type : "before-create" | "between-create-export" | "after-export" | Par défaut : "between-create-export"
ToolbarAction et ToolbarActionContext
type ToolbarAction = {
id: string;
label: string;
icon?: ReactNode;
onClick: (ctx: ToolbarActionContext) => void | Promise<void>;
disabled?: boolean | ((ctx: ToolbarActionContext) => boolean);
loading?: boolean;
variant?: "default" | "outline" | "secondary" | "ghost" | "destructive";
showInIconMode?: boolean; // true par défaut
tooltip?: string;
};
type ToolbarActionContext = {
tableId: string;
actionsAsIcons: boolean;
isMobile: boolean;
isCreateEnabled: boolean;
isExportEnabled: boolean;
isExporting: boolean;
hasListAction: boolean;
selectedRows: Row<Record<string, unknown>>[];
selectedOriginalRows: Record<string, unknown>[];
selectedRowIds: string[];
selectedCount: number;
tableActions?: TableActions;
/** La vue qu'affiche la table, telle que la rapporte `onViewConfigChange` (v3.9.0). */
getViewConfig: () => ViewConfig;
};Le code qui construit lui-même un ToolbarActionContext, comme un test, ajoute getViewConfig depuis v3.9.0.
Table Multi-Modèles
Une seule table peut garder un tableId commun tout en séparant la config table et la config formulaire :
<DataTable
tableId="cms-entries"
tableType="content-index"
formType="content-entry"
getTableConfig={(tableType) => ({
...configs[tableType],
form: {
createFormType: "content-entry",
resolveEditFormType: (row) => `${row.modelId}-entry`,
},
})}
getFormConfig={(formType, ctx) =>
buildEntryForm({
formType,
modelId: String(ctx?.values?.modelId ?? ctx?.row?.modelId ?? ""),
})
}
customBulkActions={(ctx) => [
{
id: "publish-selected",
label: "Publish",
icon: Send,
disabled: ctx.selectedCount === 0,
onClick: async () => publishEntries(ctx.selectedOriginalRows),
},
{
id: "archive-selected",
label: "Archive",
icon: Archive,
variant: "destructive",
disabled: ctx.selectedCount === 0,
confirm: {
title: "Archive selected entries?",
description: `Archive ${ctx.selectedCount} selected entries.`,
},
onClick: async () => archiveEntries(ctx.selectedOriginalRows),
},
]}
/>Ici cms-entries pilote URL/cache/sélection, content-index pilote les colonnes et filtres, et chaque ligne peut ouvrir son propre type de formulaire d'édition.
Données initiales server-first
Pour les pages Next.js App Router, chargez la première page dans un Server Component, puis passez les lignes et la pagination à un petit Client Component qui rend DataTable.
// app/products/page.tsx
import { listProducts } from "./actions/products";
import { ProductsTableClient } from "./products-table-client";
export default async function ProductsPage() {
const initial = await listProducts({ limit: 10, page: 1 });
return (
<ProductsTableClient
initialData={initial.data}
initialPageCount={initial.meta.pageCount}
initialRowCount={initial.meta.totalCount}
/>
);
}// app/products/products-table-client.tsx
"use client";
import { DataTable } from "@/components/ui/yayaw-table";
import { listProducts } from "./actions/products";
import { getTableConfig } from "./table-config";
export function ProductsTableClient({
initialData,
initialPageCount,
initialRowCount,
}: {
initialData: Record<string, unknown>[];
initialPageCount: number;
initialRowCount: number;
}) {
return (
<DataTable
getTableActions={() => ({ list: listProducts })}
getTableConfig={getTableConfig}
initialData={initialData}
initialPageCount={initialPageCount}
initialRowCount={initialRowCount}
tableType="products"
/>
);
}initialData est uniquement la première valeur de cache. Tri, filtres, pagination, retry et refresh continuent d'appeler votre action getTableActions().list via TanStack Query, donc l'autorisation et l'accès aux données restent dans votre couche serveur.
enableAdvancedFilters
Active l'UI de filtres avancés si disponible dans votre configuration.
Type : boolean | Par défaut : false
columnTypeMapping
Mappe vos types backend dynamiques vers les types de rendu internes.
Type : Record<string, 'text' | 'number' | 'date' | 'select' | 'multiSelect'> | Par défaut : {}
Usage
<DataTable
tableType="products"
loadingOverlay={<MySpinner />}
onRowSelectionChange={(rows) => console.log(rows)}
onBulkDelete={(rows) => console.log("delete", rows.length)}
onExport={(rows) => console.log("export all", rows.length)}
onBulkExport={(rows) => console.log("export selected", rows.length)}
/>Action personnalisée en mode texte :
<DataTable
tableType="products"
toolbarActions={[
{
id: "recalculate-prices",
label: "Recalculate prices",
onClick: async () => {
await recalculatePrices();
},
variant: "secondary",
},
]}
/>Action personnalisée en mode icône avec tooltip et placement explicite :
<DataTable
tableType="products"
toolbarActions={(ctx) => [
{
id: "recalculate-prices",
label: "Recalculate prices",
tooltip: "Recalculate prices",
icon: <RefreshCw className="h-4 w-4" />,
disabled: () => !ctx.hasListAction || ctx.isExporting,
onClick: async () => {
await recalculatePrices();
},
},
]}
toolbarActionsPlacement="between-create-export"
/>Compatibilité: si toolbarActions n'est pas défini, le comportement de la toolbar reste inchangé.
Voir aussi :