Supercharge your tables or build a datagrid from scratch for TS/JS, React, Vue, Solid, Svelte, Qwik, Angular, and Lit while retaining 100% control over markup and styles.
Get Startedimport { Component, signal } from '@angular/core'
import { createAngularTable, getCoreRowModel, FlexRenderDirective, type ColumnDef } from '@tanstack/angular-table'
type Person = { id: number; name: string }
@Component({
standalone: true,
selector: 'app-table',
imports: [FlexRenderDirective],
template: `
<table>
<thead>
@for (hg of table.getHeaderGroups(); track hg.id) {
<tr>
@for (header of hg.headers; track header.id) {
<th>
<ng-container *flexRender="header.column.columnDef.header; props: header.getContext()"></ng-container>
</th>
}
</tr>
}
</thead>
<tbody>
@for (row of table.getRowModel().rows; track row.id) {
<tr>
@for (cell of row.getVisibleCells(); track cell.id) {
<td>
<ng-container *flexRender="cell.column.columnDef.cell; props: cell.getContext()"></ng-container>
</td>
}
</tr>
}
</tbody>
</table>
`,
})
export class AppComponent {
data = signal<Person[]>([{ id: 1, name: 'Ada' }])
columns: ColumnDef<Person>[] = [
{ accessorKey: 'name', header: 'Name' },
]
table = createAngularTable(() => ({
data: this.data(),
columns: this.columns,
getCoreRowModel: getCoreRowModel(),
}))
}import { Component, signal } from '@angular/core'
import { createAngularTable, getCoreRowModel, FlexRenderDirective, type ColumnDef } from '@tanstack/angular-table'
type Person = { id: number; name: string }
@Component({
standalone: true,
selector: 'app-table',
imports: [FlexRenderDirective],
template: `
<table>
<thead>
@for (hg of table.getHeaderGroups(); track hg.id) {
<tr>
@for (header of hg.headers; track header.id) {
<th>
<ng-container *flexRender="header.column.columnDef.header; props: header.getContext()"></ng-container>
</th>
}
</tr>
}
</thead>
<tbody>
@for (row of table.getRowModel().rows; track row.id) {
<tr>
@for (cell of row.getVisibleCells(); track cell.id) {
<td>
<ng-container *flexRender="cell.column.columnDef.cell; props: cell.getContext()"></ng-container>
</td>
}
</tr>
}
</tbody>
</table>
`,
})
export class AppComponent {
data = signal<Person[]>([{ id: 1, name: 'Ada' }])
columns: ColumnDef<Person>[] = [
{ accessorKey: 'name', header: 'Name' },
]
table = createAngularTable(() => ({
data: this.data(),
columns: this.columns,
getCoreRowModel: getCoreRowModel(),
}))
}import { LitElement, customElement, html } from 'lit'
import { createLitTable, getCoreRowModel, flexRender } from '@tanstack/lit-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
@customElement('simple-table')
export class SimpleTable extends LitElement {
table = createLitTable({ data, columns, getCoreRowModel: getCoreRowModel() })
render() {
return html`<table>
<thead>
${this.table.getHeaderGroups().map((hg) => html`<tr>
${hg.headers.map((header) => html`<th>${flexRender(header.column.columnDef.header, header.getContext())}</th>`)}
</tr>`)}
</thead>
<tbody>
${this.table.getRowModel().rows.map((row) => html`<tr>
${row.getVisibleCells().map((cell) => html`<td>${flexRender(cell.column.columnDef.cell, cell.getContext())}</td>`)}
</tr>`)}
</tbody>
</table>`
}
}import { LitElement, customElement, html } from 'lit'
import { createLitTable, getCoreRowModel, flexRender } from '@tanstack/lit-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
@customElement('simple-table')
export class SimpleTable extends LitElement {
table = createLitTable({ data, columns, getCoreRowModel: getCoreRowModel() })
render() {
return html`<table>
<thead>
${this.table.getHeaderGroups().map((hg) => html`<tr>
${hg.headers.map((header) => html`<th>${flexRender(header.column.columnDef.header, header.getContext())}</th>`)}
</tr>`)}
</thead>
<tbody>
${this.table.getRowModel().rows.map((row) => html`<tr>
${row.getVisibleCells().map((cell) => html`<td>${flexRender(cell.column.columnDef.cell, cell.getContext())}</td>`)}
</tr>`)}
</tbody>
</table>`
}
}import { component$ } from '@builder.io/qwik'
import { createQwikTable, getCoreRowModel, flexRender } from '@tanstack/qwik-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
export default component$(() => {
const table = createQwikTable({ data, columns, getCoreRowModel: getCoreRowModel() })
return (
<table>
<thead>
{table.getHeaderGroups().map((hg) => (
<tr>
{hg.headers.map((header) => (
<th>{flexRender(header.column.columnDef.header, header.getContext())}</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr>
{row.getVisibleCells().map((cell) => (
<td>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
))}
</tbody>
</table>
)
})import { component$ } from '@builder.io/qwik'
import { createQwikTable, getCoreRowModel, flexRender } from '@tanstack/qwik-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
export default component$(() => {
const table = createQwikTable({ data, columns, getCoreRowModel: getCoreRowModel() })
return (
<table>
<thead>
{table.getHeaderGroups().map((hg) => (
<tr>
{hg.headers.map((header) => (
<th>{flexRender(header.column.columnDef.header, header.getContext())}</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr>
{row.getVisibleCells().map((cell) => (
<td>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
))}
</tbody>
</table>
)
})import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
export default function SimpleTable() {
const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
return (
<table>
<thead>
{table.getHeaderGroups().map((hg) => (
<tr key={hg.id}>
{hg.headers.map((header) => (
<th key={header.id}>
{flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
)
}import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
export default function SimpleTable() {
const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })
return (
<table>
<thead>
{table.getHeaderGroups().map((hg) => (
<tr key={hg.id}>
{hg.headers.map((header) => (
<th key={header.id}>
{flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
)
}import { createSolidTable, getCoreRowModel, flexRender } from '@tanstack/solid-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
export default function SimpleTable() {
const table = createSolidTable({ data, columns, getCoreRowModel: getCoreRowModel() })
return (
<table>
<thead>
{table.getHeaderGroups().map((hg) => (
<tr>
{hg.headers.map((header) => (
<th>{flexRender(header.column.columnDef.header, header.getContext())}</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr>
{row.getVisibleCells().map((cell) => (
<td>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
))}
</tbody>
</table>
)
}import { createSolidTable, getCoreRowModel, flexRender } from '@tanstack/solid-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
export default function SimpleTable() {
const table = createSolidTable({ data, columns, getCoreRowModel: getCoreRowModel() })
return (
<table>
<thead>
{table.getHeaderGroups().map((hg) => (
<tr>
{hg.headers.map((header) => (
<th>{flexRender(header.column.columnDef.header, header.getContext())}</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr>
{row.getVisibleCells().map((cell) => (
<td>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
))}
</tr>
))}
</tbody>
</table>
)
}<script lang="ts">
import { createSvelteTable, getCoreRowModel, flexRender } from '@tanstack/svelte-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
const table = createSvelteTable({ data, columns, getCoreRowModel: getCoreRowModel() })
</script>
<table>
<thead>
{#each table.getHeaderGroups() as hg}
<tr>
{#each hg.headers as header}
<th>{flexRender(header.column.columnDef.header, header.getContext())}</th>
{/each}
</tr>
{/each}
</thead>
<tbody>
{#each table.getRowModel().rows as row}
<tr>
{#each row.getVisibleCells() as cell}
<td>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
{/each}
</tr>
{/each}
</tbody>
</table><script lang="ts">
import { createSvelteTable, getCoreRowModel, flexRender } from '@tanstack/svelte-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
const table = createSvelteTable({ data, columns, getCoreRowModel: getCoreRowModel() })
</script>
<table>
<thead>
{#each table.getHeaderGroups() as hg}
<tr>
{#each hg.headers as header}
<th>{flexRender(header.column.columnDef.header, header.getContext())}</th>
{/each}
</tr>
{/each}
</thead>
<tbody>
{#each table.getRowModel().rows as row}
<tr>
{#each row.getVisibleCells() as cell}
<td>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
{/each}
</tr>
{/each}
</tbody>
</table><script setup lang="ts">
import { useVueTable, getCoreRowModel, flexRender } from '@tanstack/vue-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
const table = useVueTable({ data, columns, getCoreRowModel: getCoreRowModel() })
</script>
<template>
<table>
<thead>
<tr v-for="hg in table.getHeaderGroups()" :key="hg.id">
<th v-for="header in hg.headers" :key="header.id">
{{ flexRender(header.column.columnDef.header, header.getContext()) }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in table.getRowModel().rows" :key="row.id">
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
{{ flexRender(cell.column.columnDef.cell, cell.getContext()) }}
</td>
</tr>
</tbody>
</table>
</template><script setup lang="ts">
import { useVueTable, getCoreRowModel, flexRender } from '@tanstack/vue-table'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
const table = useVueTable({ data, columns, getCoreRowModel: getCoreRowModel() })
</script>
<template>
<table>
<thead>
<tr v-for="hg in table.getHeaderGroups()" :key="hg.id">
<th v-for="header in hg.headers" :key="header.id">
{{ flexRender(header.column.columnDef.header, header.getContext()) }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="row in table.getRowModel().rows" :key="row.id">
<td v-for="cell in row.getVisibleCells()" :key="cell.id">
{{ flexRender(cell.column.columnDef.cell, cell.getContext()) }}
</td>
</tr>
</tbody>
</table>
</template>import { createTable, getCoreRowModel, flexRender } from '@tanstack/table-core'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
const table = createTable({ data, columns, getCoreRowModel: getCoreRowModel() })
const thead = document.querySelector('thead')!
table.getHeaderGroups().forEach((hg) => {
const tr = document.createElement('tr')
hg.headers.forEach((header) => {
const th = document.createElement('th')
th.textContent = String(flexRender(header.column.columnDef.header, header.getContext()))
tr.appendChild(th)
})
thead.appendChild(tr)
})
const tbody = document.querySelector('tbody')!
table.getRowModel().rows.forEach((row) => {
const tr = document.createElement('tr')
row.getVisibleCells().forEach((cell) => {
const td = document.createElement('td')
td.textContent = String(flexRender(cell.column.columnDef.cell, cell.getContext()))
tr.appendChild(td)
})
tbody.appendChild(tr)
})import { createTable, getCoreRowModel, flexRender } from '@tanstack/table-core'
const data = [{ id: 1, name: 'Ada' }]
const columns = [{ accessorKey: 'name', header: 'Name' }]
const table = createTable({ data, columns, getCoreRowModel: getCoreRowModel() })
const thead = document.querySelector('thead')!
table.getHeaderGroups().forEach((hg) => {
const tr = document.createElement('tr')
hg.headers.forEach((header) => {
const th = document.createElement('th')
th.textContent = String(flexRender(header.column.columnDef.header, header.getContext()))
tr.appendChild(th)
})
thead.appendChild(tr)
})
const tbody = document.querySelector('tbody')!
table.getRowModel().rows.forEach((row) => {
const tr = document.createElement('tr')
row.getVisibleCells().forEach((cell) => {
const td = document.createElement('td')
td.textContent = String(flexRender(cell.column.columnDef.cell, cell.getContext()))
tr.appendChild(td)
})
tbody.appendChild(tr)
})See what teams are saying
"Introducing Table and Data Table components. Powered by TanStack Table. With Pagination, Row Selection, Sorting, Filters, Row Actions and Keyboard Navigation."
"I made a version using React Aria Components with arrow key navigation, multi selection, screen reader announcements, and more. Works great with TanStack Table too!"
"TanStack Table is the perfect choice if you need a lightweight, unopinionated, and fully customizable solution. It gives you the power and leaves the presentation up to you."
"Linear-style table filters using shadcn and TanStack Table. Open source. You'll be able to use this as an add-on to the Data Table component."
"Introducing Table and Data Table components. Powered by TanStack Table. With Pagination, Row Selection, Sorting, Filters, Row Actions and Keyboard Navigation."
"I made a version using React Aria Components with arrow key navigation, multi selection, screen reader announcements, and more. Works great with TanStack Table too!"
"TanStack Table is the perfect choice if you need a lightweight, unopinionated, and fully customizable solution. It gives you the power and leaves the presentation up to you."
"Linear-style table filters using shadcn and TanStack Table. Open source. You'll be able to use this as an add-on to the Data Table component."
With some basic styles, some table markup and few columns, you're already well on your way to creating a drop-dead powerful table.