Table

Bibliothèque pour la visualisation de données au format tabulaire.

@gravity-ui/table · npm package CI storybook

Installation

npm install --save @gravity-ui/table

Utilisation

import React from 'react';
import {Table, useTable} from '@gravity-ui/table';
import type {ColumnDef} from '@gravity-ui/table/tanstack';

interface Person {
  id: string;
  name: string;
  age: number;
}

const columns: ColumnDef<Person>[] = [
  {accessorKey: 'name', header: 'Nom', size: 100},
  {accessorKey: 'age', header: 'Âge', size: 100},
];

const data: Person[] = [
  {id: 'name', name: 'John', age: 23},
  {id: 'age', name: 'Michael', age: 27},
];

const BasicExample = () => {
  const table = useTable({
    columns,
    data,
  });

  return <Table table={table} />;
};

Composants

Vous pouvez utiliser deux composants Table :

  • BaseTable - un composant avec uniquement les styles de base ;
  • Table - un composant avec les styles basés sur Gravity UI.

Sélection de lignes

import {selectionColumn} from '@gravity-ui/table';
import type {RowSelectionState} from '@gravity-ui/table/tanstack';

const columns: ColumnDef<Person>[] = [
  selectionColumn as ColumnDef<Person>,
  // ...autres colonnes
];

const data: Person[] = [
  /* ... */
];

const RowSelectionExample = () => {
  const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});

  const table = useTable({
    columns,
    data,
    enableRowSelection: true,
    enableMultiRowSelection: true,
    onRowSelectionChange: setRowSelection,
    state: {
      rowSelection,
    },
  });

  return <Table table={table} />;
};

Pour utiliser le regroupement avec la sélection, utilisez le hook useRowSelectionFixedHandler. Sans lui, l'état de la case à cocher de la ligne parente sera incorrect. https://github.com/TanStack/table/issues/4878

Colonne de sélection personnalisée par plage

Le hook useToggleRangeSelectionHandler renvoie un gestionnaire de changement qui écoute les événements Shift+clic et effectue une sélection de lignes par plage. Il doit recevoir une instance de CellContext pour avoir accès aux états internes de la table et de la ligne.

import React, {type ChangeEvent, useCallback, useState} from 'react';

import {Table, useToggleRangeSelectionHandler, useTable} from '@gravity-ui/table';
import type {CellContext, ColumnDef, RowSelectionState} from '@gravity-ui/table/tanstack';
import {Checkbox, type CheckboxProps} from '@gravity-ui/uikit';

type CustomRangedSelectionCheckboxProps = Omit<CheckboxProps, 'onChange'> & {
  cellContext: CellContext<unknown, unknown>;
};

const CustomRangedSelectionCheckbox = ({
  className,
  cellContext,
  ...restProps
}: CustomRangedSelectionCheckboxProps) => {
  const rowToggleRangedSelectionHandler = useToggleRangeSelectionHandler(cellContext);

  const handleChange = useCallback(
    (event: ChangeEvent<HTMLInputElement>): void => {
      rowToggleRangedSelectionHandler(event);
    },
    [rowToggleRangedSelectionHandler],
  );

  return <Checkbox {...restProps} onChange={handleChange} />;
};

const customSelectionColumn: ColumnDef<unknown> = {
  id: '_select',
  header: ({table}) => (
    <Checkbox
      size="l"
      checked={table.getIsAllRowsSelected()}
      indeterminate={table.getIsSomeRowsSelected()}
      onChange={table.getToggleAllRowsSelectedHandler()}
    />
  ),
  cell: (cellContext) => (
    <CustomRangedSelectionCheckbox
      size="l"
      checked={cellContext.row.getIsSelected()}
      disabled={!cellContext.row.getCanSelect()}
      indeterminate={cellContext.row.getIsSomeSelected()}
      cellContext={cellContext}
    />
  ),
  size: 41,
  maxSize: 41,
  minSize: 41,
  enableResizing: false,
  enableSorting: false,
};

const columns: ColumnDef<Person>[] = [
  customSelectionColumn as ColumnDef<Person>,
  // ...autres colonnes
];

const data: Person[] = [
  /* ... */
];

const RowRangedSelectionExample = () => {
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

  const table = useTable({
    columns,
    data,
    enableRowSelection: true,
    enableMultiRowSelection: true,
    onRowSelectionChange: setRowSelection,
    state: {
      rowSelection,
    },
  });

  return <Table table={table} />;
};

Il existe également un composant RangedSelectionCheckbox qui utilise le hook en interne et accepte une instance de CellContext comme prop. Ce composant offre un raccourci pour ajouter la fonctionnalité de sélection par plage aux colonnes de sélection personnalisées.

import type {ColumnDef} from '@gravity-ui/table/tanstack';
import {RangedSelectionCheckbox, SelectionCheckbox} from '@gravity-ui/table';

export const selectionColumn: ColumnDef<unknown> = {
  id: '_select',
  header: ({table}) => (
    <SelectionCheckbox
      checked={table.getIsAllRowsSelected()}
      disabled={!table.options.enableRowSelection}
      indeterminate={table.getIsSomeRowsSelected()}
      onChange={table.getToggleAllRowsSelectedHandler()}
    />
  ),
  cell: (cellContext) => (
    <RangedSelectionCheckbox
      checked={cellContext.row.getIsSelected()}
      disabled={!cellContext.row.getCanSelect()}
      indeterminate={cellContext.row.getIsSomeSelected()}
      cellContext={cellContext}
    />
  ),
  meta: {
    hideInSettings: true,
  },
  size: 32,
  minSize: 32,
};

Par défaut, la colonne de sélection générée avec selectionColumn inclut la fonctionnalité de sélection par plage.

import {selectionColumn} from '@gravity-ui/table';
import type {ColumnDef} from '@gravity-ui/table/tanstack';

const columns: ColumnDef<Person>[] = [
  selectionColumn as ColumnDef<Person>,
  // ...autres colonnes
];

Remarque : Si la table contient des lignes imbriquées, la sélection par plage ne fonctionnera pas. Pour le moment, cela est considéré comme un comportement indéfini.

Tri

Apprenez-en plus sur les propriétés des colonnes dans la documentation de react-table ici.

import type {SortingState} from '@gravity-ui/table/tanstack';

const colonnes: ColumnDef<Person>[] = [
  /* ... */
];

const donnees: Person[] = [
  /* ... */
];

const ExempleTri = () => {
  const [tri, setTri] = React.useState<SortingState>([]);

  // Votre colonne DOIT avoir accessorFn pour que le tri soit activé

  const table = useTable({
    colonnes,
    donnees,
    enableSorting: true,
    getRowId: (item) => item.id,
    onSortingChange: setTri,
    state: {
      tri,
    },
  });

  return <Table table={table} />;
};

Si vous souhaitez trier les éléments manuellement, passez la propriété manualSorting :

const table = useTable({
  // ...
  manualSorting: true,
});

Groupement

import type {ExpandedState, Row} from '@gravity-ui/table/tanstack';

interface Personne {
  id: string;
  nom: string;
  age: number;
}

interface GroupePersonne {
  id: string;
  nom: string;
  elements: Personne[];
}

type Element = GroupePersonne | Personne;

const colonnes: ColumnDef<Element>[] = [
  {accessorKey: 'nom', header: 'Nom', size: 200},
  {accessorKey: 'age', header: 'Âge', size: 100},
];

const donnees: Element[] = [
  {
    id: 'amis',
    nom: 'Amis',
    elements: [
      {id: 'nick', nom: 'Nick', age: 25},
      {id: 'tom', nom: 'Tom', age: 21},
    ],
  },
  {
    id: 'parents',
    nom: 'Parents',
    elements: [
      {id: 'john', nom: 'John', age: 23},
      {id: 'michael', nom: 'Michael', age: 27},
    ],
  },
];

const getTitreGroupe = (row: Row<Element>) => row.getValue<string>('nom');

const ExempleGroupement = () => {
  const [etendu, setEtendu] = React.useState<ExpandedState>({});

  const table = useTable({
    colonnes,
    donnees,
    enableExpanding: true,
    getSubRows: (item) => ('elements' in item ? item.elements : undefined),
    onExpandedChange: setEtendu,
    state: {
      etendu,
    },
  });

  return <Table table={table} getGroupTitle={getTitreGroupe} />;
};

Pour utiliser le groupement avec la sélection, utilisez le hook useRowSelectionFixedHandler. Sans lui, l'état de la case à cocher de la ligne parente sera incorrect. https://github.com/TanStack/table/issues/4878

Pour activer les styles d'imbrication, passez withNestingStyles = true dans la configuration de la colonne.

Les indicateurs d'imbrication peuvent être désactivés en passant showTreeDepthIndicators = false.

Pour ajouter un contrôle permettant d'étendre/réduire les lignes, enveloppez le contenu de la cellule avec le composant TreeExpandableCell ou votre composant personnalisé similaire :

import {TreeExpandableCell} from '@gravity-ui/table';

const colonnes: ColumnDef<Element>[] = [
  {
    accessorKey: 'nom',
    header: 'Nom',
    size: 200,
    showTreeDepthIndicators: false,
    withNestingStyles: true,
    cell: ({row, info}) => (
      <TreeExpandableCell row={row}>{info.getValue<string>()}</TreeExpandableCell>
    ),
  },
  // ...autres colonnes
];

Réorganisation

import type {ReorderingProviderProps} from '@gravity-ui/table';
import {dragHandleColumn, ReorderingProvider} from '@gravity-ui/table';

const colonnes: ColumnDef<Person>[] = [
  dragHandleColumn,
  // ...autres colonnes
];

const donnees: Person[] = [
  /* ... */
];

const ExempleReorganisation = () => {
  const table = useTable({
    colonnes,
    donnees,
    getRowId: (item) => item.id,
  });

  const handleReorder = React.useCallback<
    NonNullable<ReorderingProviderProps<Person>['onReorder']>
  >(
    ({
      draggedItemKey,
      targetItemKey,
      baseItemKey,
      baseNextItemKey,
      enableNesting,
      nextChild,
      pullFromParent,
    }) => {
      // ...
    },
    [],
  );

  return (
    <ReorderingProvider table={table} onReorder={handleReorder}>
      <Table table={table} />
    </ReorderingProvider>
  );
};

Réorganisation sans poignée de glisser-déposer

Définissez dragWithoutHandle pour utiliser la ligne entière comme activateur de glisser-déposer et omettez dragHandleColumn des définitions de colonnes :

const colonnes: ColumnDef<Person>[] = [
  {accessorKey: 'nom', header: 'Nom'},
  {accessorKey: 'age', header: 'Âge'},
];

return (
  <ReorderingProvider table={table} dragWithoutHandle onReorder={handleReorder}>
    <Table table={table} />
  </ReorderingProvider>
);

Le pointeur doit se déplacer de 8 pixels avant que le glisser-déposer ne commence, de sorte que les clics normaux sur les lignes et les contrôles continuent de fonctionner. Pour exclure une partie personnalisée d'une ligne du démarrage d'un glisser-déposer, appelez preventDefault() dans son gestionnaire onPointerDown.

ReorderingProvider active le défilement automatique vertical de dnd-kit par défaut. Passez autoScroll={false} lorsque l'application fournit sa propre implémentation de défilement automatique par glisser-déposer ; l'exécution des deux simultanément peut entraîner des écritures de défilement concurrentes.

Réorganisation des colonnes

Enveloppez le tableau avec ColumnReorderingProvider pour activer la réorganisation par glisser-déposer des colonnes par leurs en-têtes.

import {ColumnReorderingProvider} from '@gravity-ui/table';

const colonnes: ColumnDef<Person>[] = [
  {accessorKey: 'nom', header: 'Nom', size: 100},
  {accessorKey: 'age', header: 'Âge', size: 100},
];

const ExempleReorganisationColonnes = () => {
  const table = useTable({
    colonnes,
    donnees,
    getRowId: (item) => item.id,
  });

  return (
    <ColumnReorderingProvider table={table}>
      <Table table={table} />
    </ColumnReorderingProvider>
  );
};

Réorganisation des lignes et des colonnes ensemble

Aninestez ColumnReorderingProvider et ReorderingProvider pour activer les deux axes de glisser-déposer simultanément. L'ordre des fournisseurs n'a pas d'importance ; ils partagent un contexte dnd-kit unique en interne.

import type {ColumnReorderingProviderProps, ReorderingProviderProps} from '@gravity-ui/table';
import {ColumnReorderingProvider, ReorderingProvider, dragHandleColumn} from '@gravity-ui/table';

const colonnes: ColumnDef<Person>[] = [
  dragHandleColumn,
  {accessorKey: 'nom', header: 'Nom'},
  {accessorKey: 'age', header: 'Âge'},
];
const CombinedReorderingExample = () => {
  const [data, setData] = React.useState(initialData);
  const [columnOrder, setColumnOrder] = React.useState<string[]>([]);

  const table = useTable({
    columns,
    data,
    getRowId: (item) => item.id,
    state: {columnOrder},
    onColumnOrderChange: setColumnOrder,
  });

  const handleRowReorder = React.useCallback<
    NonNullable<ReorderingProviderProps<Person>['onReorder']>
  >(({draggedItemKey, baseItemKey}) => {
    // update data array
  }, []);

  const handleColumnReorder = React.useCallback<
    NonNullable<ColumnReorderingProviderProps<Person>['onReorder']>
  >(({columnOrder}) => {
    setColumnOrder(columnOrder);
  }, []);

  return (
    <ColumnReorderingProvider table={table} onReorder={handleColumnReorder}>
      <ReorderingProvider table={table} onReorder={handleRowReorder}>
        <Table table={table} />
      </ReorderingProvider>
    </ColumnReorderingProvider>
  );
};

Si vous contrôlez vous-même columnOrder (par exemple, pour le rendre persistant), passez onReorder et appliquez l'ordre résultant :

const [columnOrder, setColumnOrder] = React.useState<string[]>([]);

const table = useTable({
  columns,
  data,
  state: {columnOrder},
  onColumnOrderChange: setColumnOrder,
});

return (
  <ColumnReorderingProvider
    table={table}
    onReorder={({columnOrder}) => setColumnOrder(columnOrder)}
  >
    <Table table={table} />
  </ColumnReorderingProvider>
);

API CSS :

Variable CSSDéfautDescription
--gt-table-reordering-insertion-line-color#4d8bffCouleur de la ligne d'insertion de dépôt
--gt-table-reordering-insertion-line-width2pxLargeur de la ligne d'insertion de dépôt
--gt-table-reordering-dragged-opacity0.4Opacité de la colonne déplacée
--gt-table-drag-overlay-background#fffArrière-plan de l'aperçu de déplacement
--gt-table-drag-overlay-shadow0 3px 12px rgba(0,0,0,0.15)Ombre portée de l'aperçu de déplacement
--gt-table-drag-overlay-border-radius6pxRayon de bordure de l'aperçu de déplacement

Pour interdire le réordonnancement d'une colonne spécifique, définissez enableColumnReordering: false dans sa définition de colonne. Les colonnes de remplacement (groupées) ne sont pas déplaçables. Utilisez activationDistance (par défaut 8) pour ajuster la distance que le pointeur doit parcourir avant qu'un déplacement ne commence, ce qui permet aux clics d'en-tête (comme le tri) de fonctionner.

Les colonnes épinglées peuvent également être réordonnées, mais uniquement entre elles : une colonne peut être déplacée au sein du groupe épinglé à gauche, du groupe épinglé à droite, ou du groupe central (non épinglé) — elle ne franchit jamais une limite d'épinglage par déplacement.

<ColumnReorderingProvider
  table={table}
  onReorder={({columnOrder, columnPinning, pinned}) => {
    if (pinned) {
      setColumnPinning(columnPinning);
    } else {
      setColumnOrder(columnOrder);
    }
  }}
>
  <Table table={table} />
</ColumnReorderingProvider>

Pendant le déplacement :

  • un aperçu flottant de la colonne (son en-tête plus les premières lignes) suit le pointeur dans une superposition de déplacement ;
  • la colonne déplacée devient semi-transparente ;
  • une ligne d'insertion bleue est dessinée à l'endroit où la colonne sera déposée ;
<ColumnReorderingProvider
  table={table}
  autoScroll
  dragOverlayRowCount={10}
  renderDragOverlay={({columnId}) => <CustomColumnPreview columnId={columnId} />}
>
  <Table table={table} />
</ColumnReorderingProvider>

Virtualisation

Utilisez si vous souhaitez utiliser le conteneur de grille comme élément de défilement (si vous souhaitez utiliser la fenêtre, consultez la section de virtualisation de fenêtre). Assurez-vous de définir une hauteur fixe sur le conteneur, sinon la virtualisation ne fonctionnera pas.

import {useRowVirtualizer} from '@gravity-ui/table';

const columns: ColumnDef<Person>[] = [
  /* ... */
];

const data: Person[] = [
  /* ... */
];

const VirtualizationExample = () => {
  const table = useTable({
    columns,
    data,
    getRowId: (item) => item.id,
  });

  const containerRef = React.useRef<HTMLDivElement>(null);

  const rowVirtualizer = useRowVirtualizer({
    count: table.getRowModel().rows.length,
    estimateSize: () => 20,
    overscan: 5,
    getScrollElement: () => containerRef.current,
  });

  return (
    <div ref={containerRef} style={{height: '500px', overflow: 'auto'}}>
      <Table table={table} rowVirtualizer={rowVirtualizer} />
    </div>
  );
};

Pour les tableaux qui doivent rester couverts pendant un défilement bidirectionnel continu, activez le mode adaptatif direct-DOM et utilisez des clés sémantiques stables. La même clé doit identifier une ligne avant et après un réordonnancement immuable ou une reparentage d'arbre :

const rows = table.getRowModel().rows;

const getItemKey = React.useCallback(
  (index: number) => rows[index]?.id ?? `missing:${index}`,
  [rows],
);

const rowVirtualizer = useRowVirtualizer({
  adaptiveFlushSync: true,
  count: rows.length,
  directDomUpdates: true,
  directDomUpdatesMode: 'position',
  estimateSize: () => 40,
  getItemKey,
  getScrollElement: () => containerRef.current,
  overscan: 12,
});

adaptiveFlushSync maintient une plage montée à chaud et remplit de manière synchrone uniquement une plage visible non couverte. directDomUpdates permet au virtualiseur de mettre à jour les positions des lignes et la taille du corps sans rendu React pour chaque notification de défilement. Utilisez position pour les lignes de tableau, sauf si des transformations sont requises par la disposition environnante. Gardez getRowId et getItemKey stables, et définissez count à partir du modèle de ligne aplati actuel après avoir développé, réduit, réordonné ou reparenté des lignes. Un rangeExtractor personnalisé arbitraire reste faisant autorité et désactive la planification adaptative de fenêtre.

BaseTable.canDeferOffscreenCellContent est une option supplémentaire pour les tableaux adaptatifs très larges. Retournez true uniquement pour le contenu de cellule passif dont le montage retardé ne peut pas modifier la géométrie, le focus, l'accessibilité ou l'état de l'application. Les lignes personnalisées, les lignes de groupe, les cellules épinglées et le contenu interactif ou à effets secondaires doivent rester actifs.

Avec l'extracteur de plage adaptatif par défaut, BaseTable épingle automatiquement la ligne active en cours de glissement. Un extracteur de plage personnalisé est responsable de la conservation de cette ligne. Si vous utilisez le réordonnancement avec un virtualiseur non adaptatif, conservez le même contrat sémantique getItemKey et passez l'option rangeExtractor :

import {getVirtualRowRangeExtractor} from '@gravity-ui/table';

// ...

const tableRef = React.useRef<HTMLTableElement>(null);

const rowVirtualizer = useRowVirtualizer({
  // ...
  rangeExtractor: getVirtualRowRangeExtractor(tableRef.current),
});

return (
  <TableWithReordering
    ref={tableRef}
    table={table}
    rowVirtualizer={rowVirtualizer}
    onReorder={handleReorder}
  />
);

Virtualisation de fenêtre

Utilisez si vous souhaitez utiliser la fenêtre comme élément de défilement

import {useWindowRowVirtualizer} from '@gravity-ui/table';

const columns: ColumnDef<Person>[] = [
  /* ... */
];

const data: Person[] = [
  /* ... */
];

const WindowVirtualizationExample = () => {
  const table = useTable({
    columns,
    data,
    getRowId: (item) => item.id,
  });

  const bodyRef = React.useRef<HTMLTableSectionElement>(null);

  const rowVirtualizer = useWindowRowVirtualizer({
    count: table.getRowModel().rows.length,
    estimateSize: () => 20,
    overscan: 5,
    scrollMargin: bodyRef.current?.offsetTop ?? 0,
  });

  return <Table table={table} rowVirtualizer={rowVirtualizer} bodyRef={bodyRef} />;
};

Redimensionnement

const columns: ColumnDef<Person>[] = [
  /* ... */
];

const data: Person[] = [
  /* ... */
];

const ResizingDemo = () => {
  const table = useTable({
    columns,
    data,
    enableColumnResizing: true,
    columnResizeMode: 'onChange',
  });

  return <Table table={table} />;
};

Paramètres des colonnes

const columns: ColumnDef<Person>[] = [
  // ...autres colonnes
  {
    id: 'settings_column_id',
    header: ({table}) => <TableSettings table={table} />,
    meta: {
      hideInSettings: false, // Optionnel. Permet de masquer cette colonne dans la fenêtre contextuelle des paramètres
      titleInSettings: 'ReactNode', // Optionnel. Remplace le champ d'en-tête pour la fenêtre contextuelle des paramètres (si vous avez besoin d'un contenu différent pour l'en-tête et la fenêtre contextuelle des paramètres)
    },
  }, // ou vous pouvez utiliser la fonction getSettingsColumn
];

const data: Person[] = [
  /* ... */
];

const TableSettingsDemo = () => {
  const [columnVisibility, onColumnVisibilityChange] = React.useState<VisibilityState>({
    // pour le contrôle externe et l'état initial
    column_id: false, // pour masquer par défaut
  });
  const [columnOrder, onColumnOrderChange] = React.useState<string[]>([
    /* ids des colonnes feuilles */
  ]); // pour le contrôle externe et l'état initial

  // Variante alternative pour obtenir l'état, les callbacks et définir les callbacks d'application des paramètres - en utilisant le hook useTableSettings :
  // const {state, callbacks} = useTableSettings({initialVisibility: {}, initialOrder: []})

  const table = useTable({
    columns,
    data,
    state: {
      columnVisibility,
      columnOrder,
    },
    onColumnVisibilityChange,
    onColumnOrderChange,
  });

  return <Table table={table} />;
};

Apprenez-en davantage sur le tableau et les propriétés de redimensionnement des colonnes dans la documentation de react-table docs

Problèmes connus et compatibilité

Compatibilité avec React 19 + React Compiler

⚠️ Problème connu : Il existe un problème de compatibilité connu avec React 19 et React Compiler lors de l'utilisation de @gravity-ui/table (qui est basé sur TanStack Table). Le tableau peut ne pas se réafficher lorsque les données changent. Voir le problème TanStack Table #5567 pour plus de détails.

Solution de contournement :

Si vous utilisez React 19 avec React Compiler et rencontrez des problèmes de réaffichage du tableau, vous pouvez utiliser la directive 'use no memo' dans le code de votre composant :

import React from 'react';
import {Table, useTable} from '@gravity-ui/table';
import type {ColumnDef} from '@gravity-ui/table/tanstack';

function MyTable() {
  'use no memo'; // Désactive la mémoïsation de React Compiler pour ce composant

  const [data, setData] = React.useState<Person[]>([]);

  const table = useTable({
    data,
    columns,
  });

  return <Table table={table} />;
}

Solution alternative :

Vous pouvez également mémoïser explicitement l'instance du tableau ou les données pour garantir des réaffichages corrects :

import React from 'react';
import {Table, useTable} from '@gravity-ui/table';
import type {ColumnDef} from '@gravity-ui/table/tanstack';

function MyTable() {
  const [data, setData] = React.useState<Person[]>([]);

  // Mémoïse explicitement les données pour garantir les réaffichages
  const memoizedData = React.useMemo(() => data, [data]);

  const table = useTable({
    data: memoizedData,
    columns,
  });

  return <Table table={table} />;
}

Remarque : Ce problème concerne la bibliothèque sous-jacente TanStack Table et devra y être corrigé. Les solutions de contournement ci-dessus devraient aider en attendant qu'un correctif soit disponible.

Licence

Distribué sous la licence MIT. Voir LICENSE pour les détails.

Pour les agents IA

Une grille de données sans tête, alimentée par TanStack-Table, pour les applications Gravity UI — utilisez-la pour des tableaux triables, sélectionnables, groupables, réordonnables et virtualisés au lieu de composer du balisage brut par-dessus le Table de base d'uikit.

Quand l'utiliser

  • Grands ensembles de données nécessitant une virtualisation de ligne ou de fenêtre (useRowVirtualizer, useWindowRowVirtualizer).
  • Tri, redimensionnement, réordonnancement des colonnes (ColumnReorderingProvider), épinglage et paramètres de colonnes par utilisateur (TableSettings).
  • Sélection de lignes (simple/multiple, par plage) et lignes d'arbre/groupées avec des cellules extensibles.

Quand ne pas l'utiliser

  • Un tableau simple et statique avec une poignée de lignes et aucune fonctionnalité avancée — le Table intégré d'uikit de @gravity-ui/uikit est plus léger.
  • Une liste non tabulaire — utilisez List de @gravity-ui/uikit.
  • Édition de cellules en ligne de style tableur — cette grille est axée sur la lecture/l'affichage, pas sur un tableur modifiable.

Pièges courants

  • Vous construisez le tableau avec useTable, puis vous rendez <Table table={table} />. La prop principale est table (l'instance), et non data/columns directement sur <Table>; passez data et columns à useTable.
  • Les types proviennent du sous-chemin @gravity-ui/table/tanstack. Importez ColumnDef, RowSelectionState, SortingState, etc. depuis @gravity-ui/table/tanstack, et non depuis la racine du package.
  • Le tri nécessite un accesseur. Une colonne doit avoir accessorKey/accessorFn pour que le tri fonctionne ; définissez enableSorting et fournissez getRowId.
  • React 19 + React Compiler peut sauter les re-rendus. Il s'agit d'un problème en amont de TanStack Table — ajoutez la directive 'use no memo' au composant ou mémoïzez data.
  • La sélection de plage pose problème avec les lignes imbriquées. La sélection de plage est un comportement indéfini lorsque le tableau a des lignes groupées/imbriquées ; utilisez useRowSelectionFixedHandler pour un état de case à cocher parent correct avec le regroupement.

Documentation pour les agents IA

La documentation lisible par les agents pour la version installée se trouve dans node_modules/@gravity-ui/table/build/docs/INDEX.md.

À propos de la bibliothèque
Soutenez la bibliothèque avec une étoile
Version
1.21.2
Dernière mise à jour
29.09.2026
Dépôt
github.com/gravity-ui/table
Licence
MIT License
Mainteneurs
Contributeurs