docs-i18n 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/dist/server/server.js +20 -20
- package/package.json +1 -1
- package/template/app/utils/content-loader.ts +4 -0
- package/template/app/utils/docs.server.ts +7 -0
- package/template/content/blog/en/announcing-query-v5.md +110 -0
- package/template/content/blog/en/hello-world.md +26 -0
- package/template/content/blog/en/i18n-best-practices.md +57 -0
- package/template/content/blog/en/react-query-vs-swr.md +100 -0
- package/template/content/blog/en/state-management-2024.md +143 -0
- package/template/content/blog/en/tanstack-router-1.0.md +121 -0
- package/template/content/blog/ja/announcing-query-v5.md +110 -0
- package/template/content/blog/ja/hello-world.md +26 -0
- package/template/content/blog/zh-hans/announcing-query-v5.md +93 -0
- package/template/content/blog/zh-hans/hello-world.md +26 -0
- package/template/content/docs-i18n/docs.config.json +25 -0
- package/template/content/docs-i18n/en/architecture.md +222 -0
- package/template/content/docs-i18n/en/configuration.md +331 -0
- package/template/content/docs-i18n/en/deployment.md +209 -0
- package/template/content/docs-i18n/en/getting-started.md +168 -0
- package/template/content/docs.config.json +25 -0
- package/template/content/en/admin.md +151 -0
- package/template/content/en/architecture.md +222 -0
- package/template/content/en/cli.md +269 -0
- package/template/content/en/configuration.md +331 -0
- package/template/content/en/deployment.md +209 -0
- package/template/content/en/getting-started.md +168 -0
- package/template/content/form/docs.config.json +18 -0
- package/template/content/form/en/guides/validation.md +175 -0
- package/template/content/form/en/installation.md +63 -0
- package/template/content/form/en/overview.md +71 -0
- package/template/content/form/en/quick-start.md +121 -0
- package/template/content/form/ja/installation.md +63 -0
- package/template/content/form/ja/overview.md +71 -0
- package/template/content/form/zh-hans/installation.md +63 -0
- package/template/content/form/zh-hans/overview.md +71 -0
- package/template/content/query/docs.config.json +32 -0
- package/template/content/query/en/guides/mutations.md +126 -0
- package/template/content/query/en/guides/pagination.md +98 -0
- package/template/content/query/en/guides/queries.md +120 -0
- package/template/content/query/en/installation.md +78 -0
- package/template/content/query/en/overview.md +72 -0
- package/template/content/query/en/quick-start.md +108 -0
- package/template/content/query/ja/installation.md +78 -0
- package/template/content/query/ja/overview.md +72 -0
- package/template/content/query/zh-hans/guides/mutations.md +126 -0
- package/template/content/query/zh-hans/guides/pagination.md +98 -0
- package/template/content/query/zh-hans/guides/queries.md +120 -0
- package/template/content/query/zh-hans/installation.md +95 -0
- package/template/content/query/zh-hans/overview.md +72 -0
- package/template/content/query/zh-hans/quick-start.md +108 -0
- package/template/content/router/docs.config.json +18 -0
- package/template/content/router/en/guides/routing-concepts.md +131 -0
- package/template/content/router/en/installation.md +57 -0
- package/template/content/router/en/overview.md +74 -0
- package/template/content/router/en/quick-start.md +88 -0
- package/template/content/router/ja/installation.md +57 -0
- package/template/content/router/ja/overview.md +78 -0
- package/template/content/router/zh-hans/guides/routing-concepts.md +131 -0
- package/template/content/router/zh-hans/installation.md +57 -0
- package/template/content/router/zh-hans/overview.md +81 -0
- package/template/content/router/zh-hans/quick-start.md +88 -0
- package/template/content/table/docs.config.json +18 -0
- package/template/content/table/en/guides/column-definitions.md +135 -0
- package/template/content/table/en/installation.md +56 -0
- package/template/content/table/en/overview.md +79 -0
- package/template/content/table/en/quick-start.md +112 -0
- package/template/content/table/ja/installation.md +56 -0
- package/template/content/table/ja/overview.md +79 -0
- package/template/content/table/zh-hans/installation.md +56 -0
- package/template/content/table/zh-hans/overview.md +79 -0
- package/template/content/virtual/docs.config.json +18 -0
- package/template/content/virtual/en/guides/dynamic-sizing.md +129 -0
- package/template/content/virtual/en/installation.md +57 -0
- package/template/content/virtual/en/overview.md +74 -0
- package/template/content/virtual/en/quick-start.md +114 -0
- package/template/content/virtual/ja/installation.md +57 -0
- package/template/content/virtual/ja/overview.md +74 -0
- package/template/content/virtual/zh-hans/installation.md +57 -0
- package/template/content/virtual/zh-hans/overview.md +74 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Quick Start
|
|
3
|
+
description: Get up and running with TanStack Table
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Quick Start
|
|
7
|
+
|
|
8
|
+
## Define Your Data
|
|
9
|
+
|
|
10
|
+
Start by defining the shape of your data:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
type Person = {
|
|
14
|
+
firstName: string
|
|
15
|
+
lastName: string
|
|
16
|
+
age: number
|
|
17
|
+
status: 'active' | 'inactive'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const data: Person[] = [
|
|
21
|
+
{ firstName: 'Alice', lastName: 'Johnson', age: 32, status: 'active' },
|
|
22
|
+
{ firstName: 'Bob', lastName: 'Smith', age: 45, status: 'inactive' },
|
|
23
|
+
{ firstName: 'Carol', lastName: 'Williams', age: 28, status: 'active' },
|
|
24
|
+
]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Define Columns
|
|
28
|
+
|
|
29
|
+
Use the column helper for type-safe column definitions:
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { createColumnHelper } from '@tanstack/react-table'
|
|
33
|
+
|
|
34
|
+
const columnHelper = createColumnHelper<Person>()
|
|
35
|
+
|
|
36
|
+
const columns = [
|
|
37
|
+
columnHelper.accessor('firstName', {
|
|
38
|
+
header: 'First Name',
|
|
39
|
+
cell: (info) => info.getValue(),
|
|
40
|
+
}),
|
|
41
|
+
columnHelper.accessor('lastName', {
|
|
42
|
+
header: 'Last Name',
|
|
43
|
+
}),
|
|
44
|
+
columnHelper.accessor('age', {
|
|
45
|
+
header: 'Age',
|
|
46
|
+
}),
|
|
47
|
+
columnHelper.accessor('status', {
|
|
48
|
+
header: 'Status',
|
|
49
|
+
cell: (info) => (
|
|
50
|
+
<span className={info.getValue() === 'active' ? 'text-green-600' : 'text-red-600'}>
|
|
51
|
+
{info.getValue()}
|
|
52
|
+
</span>
|
|
53
|
+
),
|
|
54
|
+
}),
|
|
55
|
+
]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Create the Table
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table'
|
|
62
|
+
|
|
63
|
+
function DataTable() {
|
|
64
|
+
const table = useReactTable({
|
|
65
|
+
data,
|
|
66
|
+
columns,
|
|
67
|
+
getCoreRowModel: getCoreRowModel(),
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<table>
|
|
72
|
+
<thead>
|
|
73
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
74
|
+
<tr key={headerGroup.id}>
|
|
75
|
+
{headerGroup.headers.map((header) => (
|
|
76
|
+
<th key={header.id}>
|
|
77
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
78
|
+
</th>
|
|
79
|
+
))}
|
|
80
|
+
</tr>
|
|
81
|
+
))}
|
|
82
|
+
</thead>
|
|
83
|
+
<tbody>
|
|
84
|
+
{table.getRowModel().rows.map((row) => (
|
|
85
|
+
<tr key={row.id}>
|
|
86
|
+
{row.getVisibleCells().map((cell) => (
|
|
87
|
+
<td key={cell.id}>
|
|
88
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
89
|
+
</td>
|
|
90
|
+
))}
|
|
91
|
+
</tr>
|
|
92
|
+
))}
|
|
93
|
+
</tbody>
|
|
94
|
+
</table>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Adding Sorting
|
|
100
|
+
|
|
101
|
+
Enable sorting by adding the sorting row model:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { getSortedRowModel } from '@tanstack/react-table'
|
|
105
|
+
|
|
106
|
+
const table = useReactTable({
|
|
107
|
+
data,
|
|
108
|
+
columns,
|
|
109
|
+
getCoreRowModel: getCoreRowModel(),
|
|
110
|
+
getSortedRowModel: getSortedRowModel(),
|
|
111
|
+
})
|
|
112
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: インストール
|
|
3
|
+
description: プロジェクトに TanStack Table をインストールする方法
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# インストール
|
|
7
|
+
|
|
8
|
+
## React
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @tanstack/react-table
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @tanstack/react-table
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
yarn add @tanstack/react-table
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bun add @tanstack/react-table
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Vue
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @tanstack/vue-table
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Solid
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @tanstack/solid-table
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Svelte
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @tanstack/svelte-table
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 必要要件
|
|
45
|
+
|
|
46
|
+
- TypeScript 4.7+(推奨)
|
|
47
|
+
- React 18+ / Vue 3+ / Solid 1.6+ / Svelte 4+
|
|
48
|
+
|
|
49
|
+
## アダプターパターン
|
|
50
|
+
|
|
51
|
+
TanStack Table はアダプターパターンを採用しています。コアロジックは `@tanstack/table-core` にあり、フレームワーク固有のパッケージが薄いラッパーを提供します:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// コアは自動的に含まれます — 別途インストールする必要はありません
|
|
55
|
+
import { getCoreRowModel } from '@tanstack/react-table'
|
|
56
|
+
```
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TanStack Table 概要
|
|
3
|
+
description: 強力なテーブルとデータグリッドを構築するためのヘッドレス UI
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TanStack Table
|
|
7
|
+
|
|
8
|
+
TanStack Table は、TS/JS、React、Vue、Solid、Svelte 向けの強力なテーブルとデータグリッドを構築するためのヘッドレス UI ライブラリです。
|
|
9
|
+
|
|
10
|
+
## 「ヘッドレス」UI とは?
|
|
11
|
+
|
|
12
|
+
ヘッドレス UI とは、UI 要素やインタラクションのロジック、状態、処理、API を提供しますが、マークアップ、スタイル、既成の実装は提供しないライブラリやユーティリティを指す用語です。
|
|
13
|
+
|
|
14
|
+
## 特徴
|
|
15
|
+
|
|
16
|
+
- **フレームワーク非依存** — コアロジックは React、Vue、Solid、Svelte、バニラ JS で動作
|
|
17
|
+
- **ソート** — カスタマイズ可能なソート関数によるマルチカラムソート
|
|
18
|
+
- **フィルタリング** — カスタムフィルター関数によるカラム・グローバルフィルタリング
|
|
19
|
+
- **ページネーション** — クライアントサイドとサーバーサイドのページネーション
|
|
20
|
+
- **行選択** — 単一および複数行の選択
|
|
21
|
+
- **カラムの順序変更とピン留め** — カラムの並べ替えとピン留め
|
|
22
|
+
- **カラムのサイズ変更** — リサイズ可能なカラム
|
|
23
|
+
- **仮想化** — 大規模データセット向けの仮想スクロール(TanStack Virtual 使用)
|
|
24
|
+
- **グルーピングと集約** — 行のグループ化とデータの集約
|
|
25
|
+
|
|
26
|
+
## 基本的な例
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { createColumnHelper, useReactTable, getCoreRowModel } from '@tanstack/react-table'
|
|
30
|
+
|
|
31
|
+
type Person = {
|
|
32
|
+
firstName: string
|
|
33
|
+
lastName: string
|
|
34
|
+
age: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const columnHelper = createColumnHelper<Person>()
|
|
38
|
+
|
|
39
|
+
const columns = [
|
|
40
|
+
columnHelper.accessor('firstName', { header: 'First Name' }),
|
|
41
|
+
columnHelper.accessor('lastName', { header: 'Last Name' }),
|
|
42
|
+
columnHelper.accessor('age', { header: 'Age' }),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
function MyTable({ data }: { data: Person[] }) {
|
|
46
|
+
const table = useReactTable({
|
|
47
|
+
data,
|
|
48
|
+
columns,
|
|
49
|
+
getCoreRowModel: getCoreRowModel(),
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<table>
|
|
54
|
+
<thead>
|
|
55
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
56
|
+
<tr key={headerGroup.id}>
|
|
57
|
+
{headerGroup.headers.map((header) => (
|
|
58
|
+
<th key={header.id}>
|
|
59
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
60
|
+
</th>
|
|
61
|
+
))}
|
|
62
|
+
</tr>
|
|
63
|
+
))}
|
|
64
|
+
</thead>
|
|
65
|
+
<tbody>
|
|
66
|
+
{table.getRowModel().rows.map((row) => (
|
|
67
|
+
<tr key={row.id}>
|
|
68
|
+
{row.getVisibleCells().map((cell) => (
|
|
69
|
+
<td key={cell.id}>
|
|
70
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
71
|
+
</td>
|
|
72
|
+
))}
|
|
73
|
+
</tr>
|
|
74
|
+
))}
|
|
75
|
+
</tbody>
|
|
76
|
+
</table>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 安装
|
|
3
|
+
description: 如何在项目中安装 TanStack Table
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 安装
|
|
7
|
+
|
|
8
|
+
## React
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @tanstack/react-table
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @tanstack/react-table
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
yarn add @tanstack/react-table
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bun add @tanstack/react-table
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Vue
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @tanstack/vue-table
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Solid
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @tanstack/solid-table
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Svelte
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @tanstack/svelte-table
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 环境要求
|
|
45
|
+
|
|
46
|
+
- TypeScript 4.7+(推荐)
|
|
47
|
+
- React 18+ / Vue 3+ / Solid 1.6+ / Svelte 4+
|
|
48
|
+
|
|
49
|
+
## 适配器模式
|
|
50
|
+
|
|
51
|
+
TanStack Table 采用适配器模式。核心逻辑位于 `@tanstack/table-core` 中,而各框架包提供轻量级的封装:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
// 核心会自动包含——无需单独安装
|
|
55
|
+
import { getCoreRowModel } from '@tanstack/react-table'
|
|
56
|
+
```
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TanStack Table 概述
|
|
3
|
+
description: 用于构建强大表格和数据网格的无头 UI 库
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TanStack Table
|
|
7
|
+
|
|
8
|
+
TanStack Table 是一个无头 UI 库,用于为 TS/JS、React、Vue、Solid 和 Svelte 构建强大的表格和数据网格。
|
|
9
|
+
|
|
10
|
+
## 什么是"无头" UI?
|
|
11
|
+
|
|
12
|
+
无头 UI 是指那些提供 UI 元素和交互的逻辑、状态、处理和 API,但不提供标记、样式或预构建实现的库和工具。
|
|
13
|
+
|
|
14
|
+
## 特性
|
|
15
|
+
|
|
16
|
+
- **框架无关** — 核心逻辑支持 React、Vue、Solid、Svelte 和原生 JS
|
|
17
|
+
- **排序** — 多列排序,支持自定义排序函数
|
|
18
|
+
- **筛选** — 列筛选和全局筛选,支持自定义筛选函数
|
|
19
|
+
- **分页** — 客户端和服务端分页
|
|
20
|
+
- **行选择** — 单行和多行选择
|
|
21
|
+
- **列排序与固定** — 重新排列和固定列
|
|
22
|
+
- **列大小调整** — 可调整列宽
|
|
23
|
+
- **虚拟化** — 大数据集的虚拟滚动(通过 TanStack Virtual)
|
|
24
|
+
- **分组与聚合** — 对行进行分组和数据聚合
|
|
25
|
+
|
|
26
|
+
## 基本示例
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { createColumnHelper, useReactTable, getCoreRowModel } from '@tanstack/react-table'
|
|
30
|
+
|
|
31
|
+
type Person = {
|
|
32
|
+
firstName: string
|
|
33
|
+
lastName: string
|
|
34
|
+
age: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const columnHelper = createColumnHelper<Person>()
|
|
38
|
+
|
|
39
|
+
const columns = [
|
|
40
|
+
columnHelper.accessor('firstName', { header: 'First Name' }),
|
|
41
|
+
columnHelper.accessor('lastName', { header: 'Last Name' }),
|
|
42
|
+
columnHelper.accessor('age', { header: 'Age' }),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
function MyTable({ data }: { data: Person[] }) {
|
|
46
|
+
const table = useReactTable({
|
|
47
|
+
data,
|
|
48
|
+
columns,
|
|
49
|
+
getCoreRowModel: getCoreRowModel(),
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<table>
|
|
54
|
+
<thead>
|
|
55
|
+
{table.getHeaderGroups().map((headerGroup) => (
|
|
56
|
+
<tr key={headerGroup.id}>
|
|
57
|
+
{headerGroup.headers.map((header) => (
|
|
58
|
+
<th key={header.id}>
|
|
59
|
+
{flexRender(header.column.columnDef.header, header.getContext())}
|
|
60
|
+
</th>
|
|
61
|
+
))}
|
|
62
|
+
</tr>
|
|
63
|
+
))}
|
|
64
|
+
</thead>
|
|
65
|
+
<tbody>
|
|
66
|
+
{table.getRowModel().rows.map((row) => (
|
|
67
|
+
<tr key={row.id}>
|
|
68
|
+
{row.getVisibleCells().map((cell) => (
|
|
69
|
+
<td key={cell.id}>
|
|
70
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
71
|
+
</td>
|
|
72
|
+
))}
|
|
73
|
+
</tr>
|
|
74
|
+
))}
|
|
75
|
+
</tbody>
|
|
76
|
+
</table>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sections": [
|
|
3
|
+
{
|
|
4
|
+
"label": "Getting Started",
|
|
5
|
+
"children": [
|
|
6
|
+
{ "label": "Overview", "to": "overview" },
|
|
7
|
+
{ "label": "Installation", "to": "installation" },
|
|
8
|
+
{ "label": "Quick Start", "to": "quick-start" }
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"label": "Guides",
|
|
13
|
+
"children": [
|
|
14
|
+
{ "label": "Dynamic Sizing", "to": "guides/dynamic-sizing" }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Dynamic Sizing
|
|
3
|
+
description: Learn how to handle dynamically sized elements with TanStack Virtual
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Dynamic Sizing
|
|
7
|
+
|
|
8
|
+
By default, TanStack Virtual assumes all items have the same size. However, many real-world use cases require items with varying heights or widths. The `measureElement` option enables dynamic sizing by measuring actual DOM elements.
|
|
9
|
+
|
|
10
|
+
## Basic Dynamic Sizing
|
|
11
|
+
|
|
12
|
+
To enable dynamic sizing, pass the `measureElement` function to your virtualizer and attach the provided ref to each item:
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { useVirtualizer } from '@tanstack/react-virtual'
|
|
16
|
+
|
|
17
|
+
function DynamicList({ items }: { items: string[] }) {
|
|
18
|
+
const parentRef = React.useRef<HTMLDivElement>(null)
|
|
19
|
+
|
|
20
|
+
const virtualizer = useVirtualizer({
|
|
21
|
+
count: items.length,
|
|
22
|
+
getScrollElement: () => parentRef.current,
|
|
23
|
+
estimateSize: () => 50, // estimated height in px
|
|
24
|
+
measureElement: (element) => {
|
|
25
|
+
return element.getBoundingClientRect().height
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
|
|
31
|
+
<div
|
|
32
|
+
style={{
|
|
33
|
+
height: `${virtualizer.getTotalSize()}px`,
|
|
34
|
+
width: '100%',
|
|
35
|
+
position: 'relative',
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
{virtualizer.getVirtualItems().map((virtualItem) => (
|
|
39
|
+
<div
|
|
40
|
+
key={virtualItem.key}
|
|
41
|
+
ref={virtualizer.measureElement}
|
|
42
|
+
data-index={virtualItem.index}
|
|
43
|
+
style={{
|
|
44
|
+
position: 'absolute',
|
|
45
|
+
top: 0,
|
|
46
|
+
left: 0,
|
|
47
|
+
width: '100%',
|
|
48
|
+
transform: `translateY(${virtualItem.start}px)`,
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
{items[virtualItem.index]}
|
|
52
|
+
</div>
|
|
53
|
+
))}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Estimated Size
|
|
61
|
+
|
|
62
|
+
The `estimateSize` function provides an initial size estimate before measurement occurs. A good estimate reduces layout shifts:
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
const virtualizer = useVirtualizer({
|
|
66
|
+
count: 10000,
|
|
67
|
+
getScrollElement: () => parentRef.current,
|
|
68
|
+
estimateSize: (index) => {
|
|
69
|
+
// Different estimates based on item type
|
|
70
|
+
if (items[index].type === 'header') return 80
|
|
71
|
+
if (items[index].type === 'image') return 200
|
|
72
|
+
return 50
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
> [!NOTE]
|
|
78
|
+
> The `estimateSize` function receives the item index, so you can provide different estimates based on item data. Better estimates lead to smoother scrolling.
|
|
79
|
+
|
|
80
|
+
## Performance Considerations
|
|
81
|
+
|
|
82
|
+
Dynamic sizing involves DOM measurement, which can be expensive. Here are some tips:
|
|
83
|
+
|
|
84
|
+
| Technique | Description |
|
|
85
|
+
|---|---|
|
|
86
|
+
| Good estimates | Provide accurate `estimateSize` values to minimize re-measurements |
|
|
87
|
+
| Overscan | Use `overscan` to render extra items outside the viewport |
|
|
88
|
+
| Stable keys | Provide stable `key` values to avoid unnecessary re-renders |
|
|
89
|
+
| CSS containment | Use `contain: strict` on the scroll container for better paint performance |
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
const virtualizer = useVirtualizer({
|
|
93
|
+
count: items.length,
|
|
94
|
+
getScrollElement: () => parentRef.current,
|
|
95
|
+
estimateSize: () => 50,
|
|
96
|
+
overscan: 5, // Render 5 extra items on each side
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Handling Resizes
|
|
101
|
+
|
|
102
|
+
When item content can change size after initial render (e.g., images loading, accordions expanding), you need to re-measure:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// Re-measure a specific item
|
|
106
|
+
virtualizer.resizeItem(index, newSize)
|
|
107
|
+
|
|
108
|
+
// Re-measure all items
|
|
109
|
+
virtualizer.measure()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> [!WARNING]
|
|
113
|
+
> Calling `measure()` on every render will cause performance issues. Only call it when you know sizes have actually changed, such as after a window resize or content update.
|
|
114
|
+
|
|
115
|
+
## Horizontal Dynamic Sizing
|
|
116
|
+
|
|
117
|
+
Dynamic sizing also works for horizontal lists. Use `horizontal: true` and measure width instead of height:
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
const virtualizer = useVirtualizer({
|
|
121
|
+
horizontal: true,
|
|
122
|
+
count: items.length,
|
|
123
|
+
getScrollElement: () => parentRef.current,
|
|
124
|
+
estimateSize: () => 100,
|
|
125
|
+
measureElement: (element) => {
|
|
126
|
+
return element.getBoundingClientRect().width
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Installation
|
|
3
|
+
description: How to install TanStack Virtual in your project
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Installation
|
|
7
|
+
|
|
8
|
+
## React
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @tanstack/react-virtual
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @tanstack/react-virtual
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
yarn add @tanstack/react-virtual
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bun add @tanstack/react-virtual
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Vue
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @tanstack/vue-virtual
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Solid
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @tanstack/solid-virtual
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Svelte
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @tanstack/svelte-virtual
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
- React 18+ / Vue 3+ / Solid 1.6+ / Svelte 4+
|
|
47
|
+
- A scroll container with a fixed height (or width for horizontal lists)
|
|
48
|
+
|
|
49
|
+
## Bundle Size
|
|
50
|
+
|
|
51
|
+
TanStack Virtual is extremely lightweight:
|
|
52
|
+
|
|
53
|
+
| Package | Size (gzipped) |
|
|
54
|
+
|---------|---------------|
|
|
55
|
+
| `@tanstack/virtual-core` | ~2.5 KB |
|
|
56
|
+
| `@tanstack/react-virtual` | ~3.5 KB |
|
|
57
|
+
| `@tanstack/vue-virtual` | ~3.5 KB |
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TanStack Virtual Overview
|
|
3
|
+
description: Headless UI for virtualizing large element lists at 60FPS
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TanStack Virtual
|
|
7
|
+
|
|
8
|
+
TanStack Virtual provides headless utilities for virtualizing long lists of elements. It handles the logic of which items to render based on scroll position, while you retain full control over markup and styles.
|
|
9
|
+
|
|
10
|
+
## Why Virtualization?
|
|
11
|
+
|
|
12
|
+
Rendering thousands of DOM nodes at once causes significant performance issues. Virtualization solves this by only rendering the items currently visible in the viewport, plus a small overscan buffer.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Framework Agnostic** — Works with React, Vue, Solid, Svelte, and vanilla JS
|
|
17
|
+
- **Vertical & Horizontal** — Virtualize lists in any direction
|
|
18
|
+
- **Grid Virtualization** — Combine vertical and horizontal virtualizers for grids
|
|
19
|
+
- **Dynamic Row Heights** — Supports variable-size items with measurement
|
|
20
|
+
- **Sticky Items** — Pin items to the top or bottom of the scroll area
|
|
21
|
+
- **Scroll Restoration** — Maintain scroll position across navigation
|
|
22
|
+
- **Tiny Bundle** — Under 5KB gzipped
|
|
23
|
+
|
|
24
|
+
## Basic Example
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { useVirtualizer } from '@tanstack/react-virtual'
|
|
28
|
+
|
|
29
|
+
function VirtualList() {
|
|
30
|
+
const parentRef = React.useRef<HTMLDivElement>(null)
|
|
31
|
+
|
|
32
|
+
const virtualizer = useVirtualizer({
|
|
33
|
+
count: 10000,
|
|
34
|
+
getScrollElement: () => parentRef.current,
|
|
35
|
+
estimateSize: () => 35,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
|
|
40
|
+
<div
|
|
41
|
+
style={{
|
|
42
|
+
height: `${virtualizer.getTotalSize()}px`,
|
|
43
|
+
width: '100%',
|
|
44
|
+
position: 'relative',
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
{virtualizer.getVirtualItems().map((virtualItem) => (
|
|
48
|
+
<div
|
|
49
|
+
key={virtualItem.key}
|
|
50
|
+
style={{
|
|
51
|
+
position: 'absolute',
|
|
52
|
+
top: 0,
|
|
53
|
+
left: 0,
|
|
54
|
+
width: '100%',
|
|
55
|
+
height: `${virtualItem.size}px`,
|
|
56
|
+
transform: `translateY(${virtualItem.start}px)`,
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
Row {virtualItem.index}
|
|
60
|
+
</div>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## When to Use TanStack Virtual
|
|
69
|
+
|
|
70
|
+
- Lists with 100+ items
|
|
71
|
+
- Tables with many rows
|
|
72
|
+
- Chat/message interfaces
|
|
73
|
+
- Infinite scrolling feeds
|
|
74
|
+
- Any UI with dynamic, large datasets
|