🪛 一个表格库的「逆袭」故事
如果你对 TanStack 的印象还停留在「那个做表格的库」,那你真的需要重新认识一下它了。
TanStack 的故事要从 2019 年说起。当时 Tanner Linsley 发布了 React Table v7,一个 headless UI 的表格库——所谓 headless,就是只提供逻辑和状态管理,不提供任何样式,开发者可以完全控制渲染。
这个理念当时还挺前卫的。但真正让 TanStack「出圈」的,是 2020 年发布的 React Query(现在的 TanStack Query)。它用一种近乎优雅的方式解决了前端数据获取和缓存的问题——服务器状态管理这个痛点终于被治好了。
从那以后,Tanner 就像打开了「潘多拉魔盒」一样,接二连三地发布了 Table、Query、Router、Form、Store、Virtual、Chart……每个产品都在各自的领域做到了顶尖水平。
到 2026 年,TanStack 已经扩张到了 12 个产品线,LogRocket 的年度趋势报告直接称它为「前端界的瑞士军刀」。
🗺️ TanStack 2026 全家桶一览
先来看一下 TanStack 当前的产品矩阵(2026 年 6 月):
| 产品 | 领域 | GitHub Stars | 一句话描述 |
|---|---|---|---|
| Query | 数据获取/缓存 | 42K+ | 服务器状态管理的终极方案 |
| Table | 数据表格 | 25K+ | Headless UI 表格的行业标准 |
| Router | 路由 | 8K+ | 类型安全的文件路由系统 |
| Form | 表单 | 6K+ | 高性能表单状态管理 |
| Store | 状态管理 | 5K+ | 极简的客户端状态方案 |
| Virtual | 虚拟列表 | 5K+ | 百万级列表渲染引擎 |
| Chart | 图表 | 4K+ | 声明式图表库 |
| AI | AI 集成 | 3K+ | AI 驱动的 UI 生成 |
| Start | 全栈框架 | 4K+ | 基于 TanStack 的全栈框架 |
| DB | 数据库 | 3K+ | 浏览器端数据库 |
今天我来重点拆解五个最值得关注的产品。
⚡ TanStack Query:数据获取的「黄金标准」
TanStack Query 是 TanStack 生态的「扛把子」。它的核心思想其实很简单——把服务器状态和客户端状态分开管理。
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// 定义 API 客户端const api = { getPosts: () => fetch('/api/posts').then(r => r.json()), createPost: (data: Post) => fetch('/api/posts', { method: 'POST', body: JSON.stringify(data) }).then(r => r.json()),};
// 在组件中使用查询function PostList() { const { data, isLoading, error } = useQuery({ queryKey: ['posts'], queryFn: api.getPosts, staleTime: 1000 * 60 * 5, // 5 分钟内不重新请求 gcTime: 1000 * 60 * 30, // 30 分钟后才从缓存中移除 refetchOnWindowFocus: true, // 用户切回页面时自动刷新 });
if (isLoading) return <Spinner />; if (error) return <ErrorBanner error={error} />;
return ( <div> {data?.map(post => <PostCard key={post.id} post={post} />)} </div> );}
// 使用 mutation 处理写入操作function CreatePostForm() { const queryClient = useQueryClient();
const mutation = useMutation({ mutationFn: api.createPost, onSuccess: () => { // 写入成功后自动刷新列表 queryClient.invalidateQueries({ queryKey: ['posts'] }); }, });
return ( <form onSubmit={(e) => { e.preventDefault(); mutation.mutate({ title: input, content: md }); }}> <input value={input} onChange={e => setInput(e.target.value)} /> <button type="submit" disabled={mutation.isPending}> {mutation.isPending ? '发布中...' : '发布'} </button> </form> );}是不是很优雅?staleTime、gcTime、refetchOnWindowFocus、invalidateQueries 这些概念一旦用上就回不去了。你的数据获取变得可预测、可缓存、可自动刷新——再也不用手动管理 loading 和 error 状态了。
📊 TanStack Table:构建企业级数据表格
TanStack Table v8 在 2025 年进行了大规模重构,2026 年的版本更加成熟。来看看怎么用它做一个带筛选、排序、分页的高性能表格:
import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel, createColumnHelper, flexRender } from '@tanstack/react-table';import { useMemo, useState } from 'react';
type User = { id: string; name: string; email: string; role: 'admin' | 'editor' | 'viewer'; lastActive: string; projects: number;};
const columnHelper = createColumnHelper<User>();
const columns = [ columnHelper.accessor('name', { header: '姓名', cell: info => <span className="font-medium">{info.getValue()}</span>, }), columnHelper.accessor('email', { header: '邮箱', enableSorting: false, // 邮箱一般不排序 }), columnHelper.accessor('role', { header: '角色', filterFn: 'equals', // 精确匹配过滤 cell: info => ( <span className={`badge badge-${info.getValue()}`}> {info.getValue()} </span> ), }), columnHelper.accessor('projects', { header: '项目数', enableColumnFilter: false, }), columnHelper.accessor('lastActive', { header: '最后活跃', sortingFn: 'datetime', cell: info => new Date(info.getValue()).toLocaleDateString('zh-CN'), }),];
function UserTable({ data }: { data: User[] }) { const [sorting, setSorting] = useState([]); const [globalFilter, setGlobalFilter] = useState('');
const table = useReactTable({ data, columns, state: { sorting, globalFilter }, onSortingChange: setSorting, onGlobalFilterChange: setGlobalFilter, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getFilteredRowModel: getFilteredRowModel(), getPaginationRowModel: getPaginationRowModel(), initialState: { pagination: { pageSize: 20 } }, });
return ( <div> <input placeholder="全局搜索..." value={globalFilter} onChange={e => setGlobalFilter(e.target.value)} className="search-input" /> <table> <thead> {table.getHeaderGroups().map(headerGroup => ( <tr key={headerGroup.id}> {headerGroup.headers.map(header => ( <th key={header.id} onClick={header.column.getToggleSortingHandler()}> {flexRender(header.column.columnDef.header, header.getContext())} {{ asc: ' 🔼', desc: ' 🔽' }[header.column.getIsSorted() as string] ?? null} </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> <div className="pagination"> <button onClick={() => table.setPageIndex(0)} disabled={!table.getCanPreviousPage()}> ⏮ </button> <button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}> ◀ </button> <span>第 {table.getState().pagination.pageIndex + 1} / {table.getPageCount()} 页</span> <button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}> ▶ </button> <button onClick={() => table.setPageIndex(table.getPageCount() - 1)} disabled={!table.getCanNextPage()}> ⏭ </button> </div> </div> );}几十行代码就实现了一个带全局搜索、列排序、分页的企业级表格——而且完全没有样式依赖,你可以用任何 UI 库给它穿衣服。
🔮 TanStack AI:2026 年的新面孔
2026 年最让人惊喜的新产品是 TanStack AI。它是一个「AI 驱动的 UI 生成」工具——你描述需求,它生成组件代码。
import { useAI } from '@tanstack/ai';
function AIDataDashboard() { const { component, isLoading } = useAI({ prompt: "创建一个销售仪表盘卡片,显示本周收入、订单数、转化率三个指标,每个指标用数字+小趋势图展示", framework: 'react', designSystem: 'shadcn', onGenerate: (code) => { console.log('生成的代码:', code); } });
if (isLoading) return <Skeleton />;
return <div dangerouslySetInnerHTML={{ __html: component }} />;}当然,TanStack AI 目前还处于早期阶段,但它代表了一个明确的方向——「声明式 UI」的下一个进化,就是从「写代码实现」变成「描述需求自动生成」。
🚀 TanStack Start + Router:全栈框架的新选择
TanStack Start 是 2026 年推出的全栈框架,可以理解为「TanStack 版本的 Next.js」,但它有几个独特的设计哲学:
- 文件路由 + 类型安全——路由参数自动推导类型
- Streaming SSR 默认开启——不需要额外配置
- 100% TanStack 原生集成——Query、Form、Store 天然互通
// app/routes/posts/$postId.tsximport { createFileRoute } from '@tanstack/react-router';import { useSuspenseQuery } from '@tanstack/react-query';
// 路由参数自动类型安全export const Route = createFileRoute('/posts/$postId')({ // 服务端数据预取 loader: async ({ params, context }) => { return context.queryClient.fetchQuery({ queryKey: ['post', params.postId], queryFn: () => fetch(`/api/posts/${params.postId}`).then(r => r.json()), }); }, component: PostDetail,});
function PostDetail() { const { postId } = Route.useParams(); // 类型安全! const { data } = useSuspenseQuery({ queryKey: ['post', postId], });
return ( <article> <h1>{data.title}</h1> <div dangerouslySetInnerHTML={{ __html: data.content }} /> </article> );}💡 如何上手 TanStack 生态?
如果你是个 TanStack 新手,我建议按这个顺序学习:
- 先学 TanStack Query —— 这是最有价值、学习曲线最平缓的
- 再学 TanStack Table —— 当你的项目需要展示数据时用
- 接着学 TanStack Form —— 处理复杂表单场景
- 最后看 TanStack Router/Start —— 当你想尝试全栈方案时
千万别一次性学全家桶。TanStack 的强大之处就在于每个产品都可以独立使用。今天项目中引入一个 Query,明天加一个 Table,后天试试 Form——渐进式采用才是正确的打开方式。
🎯 总结
从 2019 年的一个表格库,到 2026 年的十几款产品组成的生态系统,TanStack 的成长史其实就是前端开发「工程化」的一个缩影——从「手写一切」到「用最佳实践组装」。
Tanner Linsley 和他的团队正在做的事情,本质上是在定义一个「现代前端应用的标准栈」。而越来越多的开发者用脚投票,让这个栈变得越来越主流。
如果你还没试过 TanStack 生态中的任何产品,今天就是一个好日子。从 Query 开始,你会打开一扇新的大门。🚪