🌲系列一:跟着官方示例学习 @tanStack-table --- Basic
🌲系列二:跟着官方示例学习 @tanStack-table --- Header Groups
🌲系列三:跟着官方示例学习 @tanStack-table --- Column Filters
🌲系列四:跟着官方示例学习 @tanStack-table --- Column Ordering
🌲系列五:跟着官方示例学习 @tanStack-table --- Sticky Column Pinning
🌲系列六:跟着官方示例学习 @tanStack-table --- Column Sizing
🌲系列七:跟着官方示例学习 @tanStack-table --- Expanding
🌲系列八:跟着官方示例学习 @tanStack-table --- Pagination
🌲系列九:跟着官方示例学习 @tanStack-table --- Row Dnd
📌 固定某一行?Row Pinning 来帮你!
有时候,用户想把"最重要的那几条数据"钉在表格顶部或底部,比如:
-
总结统计行
-
当前任务的高优先级事项
-
某位特别"显眼"的
VIP
用户
这时候就轮到 @tanstack/react-table
的 Row Pinning
功能登场啦!
🧠 Row Pinning 是什么?
Row Pinning
是指将某些行固定在表格顶部(top
)或底部(bottom
),不受分页、排序或滚动的影响。 这与常见的"header
固定"不一样,我们这次是固定行,而不是列或表头。
🧩 基础状态管理
我们先为表格添加 rowPinning
状态:
tsx
const [rowPinning, setRowPinning] = React.useState<RowPinningState>({
top: [],
bottom: [],
});
然后在 useReactTable
中绑定这两个字段:
tsx
const table = useReactTable({
data,
columns,
state: { rowPinning },
onRowPinningChange: setRowPinning,
getCoreRowModel: getCoreRowModel(),
});
🧲 Pin 按钮:行控制面板!
我们给每一行加上 Pin 按钮,当点击时可以把行钉到 top / bottom:
tsx
{
id: "pin",
header: () => "Pin",
cell: ({ row }) =>
row.getIsPinned() ? (
<button onClick={() => row.pin(false)}>❌</button>
) : (
<div style={{ display: "flex", gap: "4px" }}>
<button onClick={() => row.pin("top")}>⬆️</button>
<button onClick={() => row.pin("bottom")}>⬇️</button>
</div>
),
}
-
row.getIsPinned()
:返回"top"
、"bottom"
或false
-
row.pin(position)
:将一行固定到"顶部"或"底部",如果传入false
则将该行解除固定到中心。
🧱 表格分段渲染:Top / Center / Bottom
我们要分别渲染 3 段行:
tsx
<tbody>
{table.getTopRows().map((row) => (
<PinnedRow key={row.id} row={row} table={table} />
))}
{table.getCenterRows().map((row) => (
<tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
{table.getBottomRows().map((row) => (
<PinnedRow key={row.id} row={row} table={table} />
))}
</tbody>
-
getTopRows()
: 返回被钉在顶部的行 -
getCenterRows()
: 返回正常的、未钉住的行 -
getBottomRows()
: 返回被钉在底部的行
📌 PinnedRow:钉住的行要特别处理!
tsx
function PinnedRow({ row, table }: { row: Row<any>; table: Table<any> }) {
return (
<tr
style={{
backgroundColor: "lightblue",
position: "sticky",
top:
row.getIsPinned() === "top"
? `${row.getPinnedIndex() * 26 + 48}px`
: undefined,
bottom:
row.getIsPinned() === "bottom"
? `${
(table.getBottomRows().length - 1 - row.getPinnedIndex()) * 26
}px`
: undefined,
}}
>
{row.getVisibleCells().map((cell) => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
);
}
我们用了 position: sticky
让 pinned
行始终在视野范围内,row.getPinnedIndex()
告诉你它是第几个被 pin
的。

官方代码地址🔗: @tanStack/react-table
🚀 Row Pinning 进阶技巧:行固定状态的持久化
🔄 Keep Pinned Rows:钉住之后不怕翻页!
场景: 用户翻页、过滤数据后,钉住的行就"消失"了?!
你可以通过以下配置让 pinned 行持久存在:
tsx
keepPinnedRows: true
🔧 这就让 pinned 行在任何筛选、翻页后都不会被清除,是不是很贴心?

🌳 2. Include Leaf Rows / Parent Rows:树形结构的固定控制
如果你在使用嵌套数据(也就是 getSubRows
),你一定会遇到这种情况:
"我钉住一个父节点,它下面的子节点要不要跟着一起固定?"
或者反过来:
"我钉住一个子节点,它的父节点也要固定吗?"
你可以通过两个配置来控制这类行为:
-
includeLeafRows
: 当固定父节点时,是否自动固定它的所有叶子子节点 -
includeParentRows
: 当固定一个子节点时,是否自动固定它的父节点
tsx
<input
type="checkbox"
checked={includeLeafRows}
onChange={() => setIncludeLeafRows(!includeLeafRows)}
/>
<label>Include Leaf Rows When Pinning Parent</label>
<input
type="checkbox"
checked={includeParentRows}
onChange={() => setIncludeParentRows(!includeParentRows)}
/>
<label>Include Parent Rows When Pinning Child</label>
这两个参数都会作为 row.pin()
的参数传入,用来控制行为:
tsx
row.pin("top", includeLeafRows, includeParentRows)

🪞 3. Copy Pinned Rows:固定了还要在中间再来一份?
默认情况下,getCenterRows()
只返回未被钉住的行。如果你想在中间区域也保留 pinned
行的副本,可以设置:
tsx
copyPinnedRows: true
然后用条件渲染逻辑:
tsx
{(copyPinnedRows
? table.getRowModel().rows // 所有行,包括 pinned
: table.getCenterRows() // 只显示未 pinned 的行
).map(...)}
🔁 这样用户即使滚动表格,也能一直看到 pinned
行的副本,非常适合对比查看!

官方代码地址🔗: @tanStack/react-table