Skip to content

Column Pinning Table

Pin columns to the left or right edge for easy reference while scrolling.

Open in
Preview with Controlled State
Open in

Column pinning keeps important columns visible while users scroll horizontally. Pin identifier columns (like Order ID) to the left and action columns to the right.

  1. Add the required components:
npx shadcn@latest add table input button dropdown-menu badge
  1. Add tanstack/react-table dependency:
npm install @tanstack/react-table
  1. Copy the DataTable components into your project. See the Installation Guide for detailed instructions.

We’ll build a table showing orders. Here’s our data:

type Order = {
id: string
customer: string
product: string
amount: number
status: "pending" | "shipped" | "delivered" | "cancelled"
date: string
region: string
}
const data: Order[] = [
{
id: "ORD-001",
customer: "John Doe",
product: "Premium Widget",
amount: 299.99,
status: "delivered",
date: "2024-01-15",
region: "North America",
},
// ...
]

Set initialState.columnPinning to pin columns on mount:

column-pinning.tsx
<DataTableRoot
data={data}
columns={columns}
initialState={{
columnPinning: {
left: ["id"], // Pin Order ID to left
right: ["actions"], // Pin actions to right
},
}}
>
<DataTable>
<DataTableHeader />
<DataTableBody />
</DataTable>
</DataTableRoot>

Set explicit size for each column to enable horizontal scrolling:

columns.tsx
const columns: DataTableColumnDef<Order>[] = [
{
accessorKey: "id",
size: 110,
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle title="Order ID" />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Order ID" },
},
{
accessorKey: "customer",
size: 160,
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle title="Customer" />
<DataTableColumnSortMenu />
</DataTableColumnHeader>
),
meta: { label: "Customer" },
},
// ... more columns
]

Add pinning options to column actions for user-controlled pinning:

{
accessorKey: "customer",
size: 160,
header: () => (
<DataTableColumnHeader>
<DataTableColumnTitle title="Customer" />
<DataTableColumnActions>
<DataTableColumnSortOptions />
<DataTableColumnPinOptions />
</DataTableColumnActions>
</DataTableColumnHeader>
),
}

Manage pinning state externally:

column-pinning-state.tsx
import { useState } from "react"
import { type ColumnPinningState } from "@tanstack/react-table"
export function ControlledPinningTable({ data }: { data: Order[] }) {
const [columnPinning, setColumnPinning] = useState<ColumnPinningState>({
left: ["id"],
right: ["actions"],
})
return (
<DataTableRoot
data={data}
columns={columns}
state={{ columnPinning }}
onColumnPinningChange={setColumnPinning}
>
{/* ... */}
</DataTableRoot>
)
}

✅ Use Column Pinning when:

  • Tables have many columns requiring horizontal scroll
  • Key identifiers (ID, Name) must stay visible
  • Actions column should be always accessible

❌ Consider other options when:

  • All columns fit on screen (no scrolling needed)
  • Mobile-first design (pinning adds complexity)