Skip to content

Getting Started with TanStack

TanStack is a popular framework for building table data layers. However, RevoGrid offers a more comprehensive solution by incorporating all the features of TanStack, along with advanced rendering, virtual scrolling, and column-level optimizations for superior performance. If you're transitioning from an existing TanStack architecture or want to quickly prototype, this guide will help you integrate TanStack with RevoGrid.

We strongly recommend using RevoGrid's native features for the best performance. It is specifically designed to handle advanced rendering and optimize every aspect of DOM manipulation, ensuring an efficient experience. RevoGrid operates on the column level, which allows for superior performance, whereas TanStack manages data at the row level.

The following example shows how to use TanStack with RevoGrid to create columns, bind data, and manage interactions.

Source code Git
vue
<template>
    <VGrid
        :source="source"
        :columns="rvColumns"
        hide-attribution
        @beforeedit="handleEdit"
    />
</template>

<script setup lang="ts">
import { VGrid, type ColumnProp } from '@revolist/vue3-datagrid'

import {
    useVueTable,
    getCoreRowModel,
    type ColumnDef,
    type RowData,
} from '@tanstack/vue-table'

import { computed, ref } from 'vue'
import { tanstackToRvGridColumns } from '../tanstack/utils'
import { makeData, type Person } from '../makeData'

declare module '@tanstack/vue-table' {
    interface TableMeta<TData extends RowData> {
        updateData: (
            rowIndex: number,
            columnId: ColumnProp,
            value: unknown
        ) => void
    }
}

// Define columns according tanstack guide
const columns: ColumnDef<Person>[] = [
    {
        header: 'Name',
        columns: [
            {
                accessorKey: 'firstName',
                header: 'First Name',
            },
            {
                accessorKey: 'lastName',
                header: 'Last Name',
            },
        ],
    },
]
const data = ref<Person[]>(makeData(5))

// define tanstack table
const table = useVueTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    meta: {
        updateData: (rowIndex: number, columnId: ColumnProp, value: any) => {
            data.value = data.value.map((row, index) => {
                if (index === rowIndex) {
                    return {
                        ...data.value[rowIndex]!,
                        [columnId]: value,
                    }
                }
                return row
            })
        },
    },
})

// get data from tanstack table
const source = computed(() => {
    const rows = table.getRowModel().flatRows
    return rows
})

const rvColumns = computed(() => {
    // transform tanstack to revogrid format
    return tanstackToRvGridColumns(table.getAllColumns())
})

function handleEdit(e: CustomEvent<HTMLRevoGridElementEventMap['beforeedit']>) {
    e.preventDefault()
    table.options.meta?.updateData(
        e.detail.model.index,
        e.detail.prop,
        e.detail.val
    )
}
</script>
tsx
import React, { useState, useMemo, useCallback } from 'react';
import { RevoGrid, type BeforeSaveDataDetails, type RevoGridCustomEvent } from '@revolist/react-datagrid';
import {
  useReactTable,
  getCoreRowModel,
  type ColumnDef,
} from '@tanstack/react-table';
import { tanstackToRvGridColumns } from '../tanstack/utils';
import { makeData, type Person } from '../makeData';

// Define TanStack columns
const columns: ColumnDef<Person>[] = [
  {
    header: 'Name',
    columns: [
      {
        accessorKey: 'firstName',
        header: 'First Name',
      },
      {
        accessorKey: 'lastName',
        header: 'Last Name',
      },
    ],
  },
];

const ExampleIntegration = () => {
  const [data, setData] = useState(makeData(5));

  // Set up TanStack table instance
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    meta: {
      updateData: (rowIndex, columnId, value) => {
        setData((old) =>
          old.map((row, index) => {
            if (index === rowIndex) {
              return {
                ...old[rowIndex],
                [columnId]: value,
              };
            }
            return row;
          })
        );
      },
    },
  });

  // Extract data and columns for RevoGrid
  const source = useMemo(() => {
    const rows = table.getRowModel().flatRows;
    return rows;
  }, [table]);

  const rvColumns = useMemo(() => {
    // Convert TanStack column definition to RevoGrid format
    return tanstackToRvGridColumns(table.getAllColumns());
  }, [table]);

  const handleEdit = useCallback((e: RevoGridCustomEvent<BeforeSaveDataDetails>) => {
    e.preventDefault();
    const { model, prop, val } = e.detail;
    table.options.meta?.updateData(model.index, prop, val);
  }, [table]);

  return (
    <RevoGrid
      source={source}
      columns={rvColumns}
      onBeforeedit={handleEdit}
      hideAttribution
    />
  );
};

export default ExampleIntegration;
ts
// Helper functions to convert tanstack to revogrid format
import type { ColumnGrouping, ColumnRegular } from "@revolist/vue3-datagrid";
import type { Column } from "@tanstack/vue-table";

export function tanstackToRvGridColumns(tanstackColumns: Column<any>[]) {
  return tanstackColumns.map((col) => {
    // Check if this column has children (group columns)
    if (col.columns?.length) {
      const rvGroup: ColumnGrouping = {
        name: col.columnDef.header,
        children: tanstackToRvGridColumns(col.columns),
      };
      return rvGroup; // Recursive call for child columns
    }
    const rvColumn: ColumnRegular = {
      prop: col.id, // Use col.id or accessorKey
      name: col.columnDef.header, // Direct mapping for column header
      sortable: !!col.getCanSort(), // Check if the column is sortable
      filter: col.getCanFilter() ? "customFilterType" : false, // Handle filters
    };

    // Handle custom sorting functions via cellCompare
    const sortingFn = col.getSortingFn();
    if (!!sortingFn) {
      rvColumn.cellCompare = (_, rowA: any, rowB: any) =>
        sortingFn(rowA, rowB, col.id);
      rvColumn.sortable = true; // Ensure sorting is enabled
    }

    // Handle filtering logic if provided
    const filterFn = col.getFilterFn();
    if (filterFn) {
      rvColumn.filter = "customFilterType"; // Assume custom type for filtering
    }

    rvColumn.cellParser = (model) => {
      return model.renderValue(col.id);
    };

    return rvColumn;
  });
}
ts
import { faker } from '@faker-js/faker'

export type Person = {
  avatar: string
  firstName: string
  lastName: string
  name: string
  age: number
  visits: number
  progress: number
  status: 'relationship' | 'complicated' | 'single'
  subRows?: Person[]
}

const range = (len: number) => {
  const arr: number[] = []
  for (let i = 0; i < len; i++) {
    arr.push(i)
  }
  return arr
}

const newPerson = (): Person => {
  const firstName = faker.person.firstName()
  const lastName = faker.person.lastName()
  return {
    avatar: faker.image.avatar(),
    firstName,
    lastName,
    name: `${firstName} ${lastName}`,
    age: faker.number.int(40),
    visits: faker.number.int(1000),
    progress: faker.number.int(100),
    status: faker.helpers.shuffle<Person['status']>([
      'relationship',
      'complicated',
      'single',
    ])[0]!,
  }
}

export function makeData(...lens: number[]) {
  const makeDataLevel = (depth = 0): Person[] => {
    const len = lens[depth]!
    return range(len).map((): Person => {
      return {
        ...newPerson(),
        subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
      }
    })
  }

  return makeDataLevel()
}

Summary

Integrating TanStack with RevoGrid allows you to leverage the row-level operations provided by TanStack, while taking advantage of RevoGrid's optimized performance and advanced rendering capabilities, including virtual scrolling and efficient DOM handling. For a smooth migration or quick experimentation, this setup provides a great foundation, but remember, RevoGrid's built-in features are crafted for peak performance and user experience.

Revogrid is a powerful data grid library made by Revolist OU.